| OLD | NEW |
| 1 // Copyright (c) 2013, 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 "platform/address_sanitizer.h" | 5 #include "vm/profiler_service.h" |
| 6 #include "platform/memory_sanitizer.h" | |
| 7 #include "platform/utils.h" | |
| 8 | 6 |
| 9 #include "vm/allocation.h" | 7 #include "vm/growable_array.h" |
| 10 #include "vm/atomic.h" | |
| 11 #include "vm/code_patcher.h" | |
| 12 #include "vm/isolate.h" | |
| 13 #include "vm/json_stream.h" | |
| 14 #include "vm/lockers.h" | |
| 15 #include "vm/native_symbol.h" | 8 #include "vm/native_symbol.h" |
| 16 #include "vm/object.h" | 9 #include "vm/object.h" |
| 17 #include "vm/os.h" | 10 #include "vm/os.h" |
| 18 #include "vm/profiler.h" | 11 #include "vm/profiler.h" |
| 19 #include "vm/reusable_handles.h" | 12 #include "vm/reusable_handles.h" |
| 20 #include "vm/signal_handler.h" | 13 #include "vm/scope_timer.h" |
| 21 #include "vm/simulator.h" | |
| 22 #include "vm/stack_frame.h" | |
| 23 | 14 |
| 24 namespace dart { | 15 namespace dart { |
| 25 | 16 |
| 26 | 17 DECLARE_FLAG(int, profile_depth); |
| 27 #if defined(TARGET_OS_ANDROID) || defined(HOST_ARCH_ARM64) | 18 DECLARE_FLAG(bool, trace_profiler); |
| 28 DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); | 19 DECLARE_FLAG(int, profile_period); |
| 29 #else | |
| 30 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); | |
| 31 #endif | |
| 32 DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); | |
| 33 DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); | |
| 34 DEFINE_FLAG(int, profile_period, 1000, | |
| 35 "Time between profiler samples in microseconds. Minimum 50."); | |
| 36 DEFINE_FLAG(int, profile_depth, 8, | |
| 37 "Maximum number stack frames walked. Minimum 1. Maximum 255."); | |
| 38 #if defined(PROFILE_NATIVE_CODE) || defined(USING_SIMULATOR) | |
| 39 DEFINE_FLAG(bool, profile_vm, true, | |
| 40 "Always collect native stack traces."); | |
| 41 #else | |
| 42 DEFINE_FLAG(bool, profile_vm, false, | |
| 43 "Always collect native stack traces."); | |
| 44 #endif | |
| 45 | |
| 46 bool Profiler::initialized_ = false; | |
| 47 SampleBuffer* Profiler::sample_buffer_ = NULL; | |
| 48 | |
| 49 void Profiler::InitOnce() { | |
| 50 // Place some sane restrictions on user controlled flags. | |
| 51 SetSamplePeriod(FLAG_profile_period); | |
| 52 SetSampleDepth(FLAG_profile_depth); | |
| 53 Sample::InitOnce(); | |
| 54 if (!FLAG_profile) { | |
| 55 return; | |
| 56 } | |
| 57 ASSERT(!initialized_); | |
| 58 sample_buffer_ = new SampleBuffer(); | |
| 59 NativeSymbolResolver::InitOnce(); | |
| 60 ThreadInterrupter::SetInterruptPeriod(FLAG_profile_period); | |
| 61 ThreadInterrupter::Startup(); | |
| 62 initialized_ = true; | |
| 63 } | |
| 64 | |
| 65 | |
| 66 void Profiler::Shutdown() { | |
| 67 if (!FLAG_profile) { | |
| 68 return; | |
| 69 } | |
| 70 ASSERT(initialized_); | |
| 71 ThreadInterrupter::Shutdown(); | |
| 72 NativeSymbolResolver::ShutdownOnce(); | |
| 73 } | |
| 74 | |
| 75 | |
| 76 void Profiler::SetSampleDepth(intptr_t depth) { | |
| 77 const int kMinimumDepth = 1; | |
| 78 const int kMaximumDepth = 255; | |
| 79 if (depth < kMinimumDepth) { | |
| 80 FLAG_profile_depth = kMinimumDepth; | |
| 81 } else if (depth > kMaximumDepth) { | |
| 82 FLAG_profile_depth = kMaximumDepth; | |
| 83 } else { | |
| 84 FLAG_profile_depth = depth; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 | |
| 89 void Profiler::SetSamplePeriod(intptr_t period) { | |
| 90 const int kMinimumProfilePeriod = 50; | |
| 91 if (period < kMinimumProfilePeriod) { | |
| 92 FLAG_profile_period = kMinimumProfilePeriod; | |
| 93 } else { | |
| 94 FLAG_profile_period = period; | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 | |
| 99 void Profiler::InitProfilingForIsolate(Isolate* isolate, bool shared_buffer) { | |
| 100 if (!FLAG_profile) { | |
| 101 return; | |
| 102 } | |
| 103 ASSERT(isolate == Isolate::Current()); | |
| 104 ASSERT(isolate != NULL); | |
| 105 ASSERT(sample_buffer_ != NULL); | |
| 106 { | |
| 107 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | |
| 108 SampleBuffer* sample_buffer = sample_buffer_; | |
| 109 if (!shared_buffer) { | |
| 110 sample_buffer = new SampleBuffer(); | |
| 111 } | |
| 112 IsolateProfilerData* profiler_data = | |
| 113 new IsolateProfilerData(sample_buffer, !shared_buffer); | |
| 114 ASSERT(profiler_data != NULL); | |
| 115 isolate->set_profiler_data(profiler_data); | |
| 116 if (FLAG_trace_profiled_isolates) { | |
| 117 OS::Print("Profiler Setup %p %s\n", isolate, isolate->name()); | |
| 118 } | |
| 119 } | |
| 120 BeginExecution(isolate); | |
| 121 } | |
| 122 | |
| 123 | |
| 124 void Profiler::ShutdownProfilingForIsolate(Isolate* isolate) { | |
| 125 ASSERT(isolate != NULL); | |
| 126 if (!FLAG_profile) { | |
| 127 return; | |
| 128 } | |
| 129 // We do not have a current isolate. | |
| 130 ASSERT(Isolate::Current() == NULL); | |
| 131 { | |
| 132 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | |
| 133 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 134 if (profiler_data == NULL) { | |
| 135 // Already freed. | |
| 136 return; | |
| 137 } | |
| 138 isolate->set_profiler_data(NULL); | |
| 139 delete profiler_data; | |
| 140 if (FLAG_trace_profiled_isolates) { | |
| 141 OS::Print("Profiler Shutdown %p %s\n", isolate, isolate->name()); | |
| 142 } | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 | |
| 147 void Profiler::BeginExecution(Isolate* isolate) { | |
| 148 if (isolate == NULL) { | |
| 149 return; | |
| 150 } | |
| 151 if (!FLAG_profile) { | |
| 152 return; | |
| 153 } | |
| 154 ASSERT(initialized_); | |
| 155 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 156 if (profiler_data == NULL) { | |
| 157 return; | |
| 158 } | |
| 159 ThreadInterrupter::Register(RecordSampleInterruptCallback, isolate); | |
| 160 ThreadInterrupter::WakeUp(); | |
| 161 } | |
| 162 | |
| 163 | |
| 164 void Profiler::EndExecution(Isolate* isolate) { | |
| 165 if (isolate == NULL) { | |
| 166 return; | |
| 167 } | |
| 168 if (!FLAG_profile) { | |
| 169 return; | |
| 170 } | |
| 171 ASSERT(initialized_); | |
| 172 ThreadInterrupter::Unregister(); | |
| 173 } | |
| 174 | |
| 175 | |
| 176 class ScopeStopwatch : public ValueObject { | |
| 177 public: | |
| 178 explicit ScopeStopwatch(const char* name) : name_(name) { | |
| 179 start_ = FLAG_trace_profiler ? OS::GetCurrentTimeMillis() : 0; | |
| 180 } | |
| 181 | |
| 182 int64_t GetElapsed() const { | |
| 183 int64_t end = OS::GetCurrentTimeMillis(); | |
| 184 ASSERT(end >= start_); | |
| 185 return end - start_; | |
| 186 } | |
| 187 | |
| 188 ~ScopeStopwatch() { | |
| 189 if (FLAG_trace_profiler) { | |
| 190 int64_t elapsed = GetElapsed(); | |
| 191 OS::Print("%s took %" Pd64 " millis.\n", name_, elapsed); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 private: | |
| 196 const char* name_; | |
| 197 int64_t start_; | |
| 198 }; | |
| 199 | |
| 200 | 20 |
| 201 struct AddressEntry { | 21 struct AddressEntry { |
| 202 uword pc; | 22 uword pc; |
| 203 intptr_t exclusive_ticks; | 23 intptr_t exclusive_ticks; |
| 204 intptr_t inclusive_ticks; | 24 intptr_t inclusive_ticks; |
| 205 | 25 |
| 206 void tick(bool exclusive) { | 26 void tick(bool exclusive) { |
| 207 if (exclusive) { | 27 if (exclusive) { |
| 208 exclusive_ticks++; | 28 exclusive_ticks++; |
| 209 } else { | 29 } else { |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 { | 350 { |
| 531 // Generate a fake function entry. | 351 // Generate a fake function entry. |
| 532 JSONObject func(&obj, "function"); | 352 JSONObject func(&obj, "function"); |
| 533 func.AddProperty("type", "@Function"); | 353 func.AddProperty("type", "@Function"); |
| 534 func.AddProperty("kind", "Tag"); | 354 func.AddProperty("kind", "Tag"); |
| 535 obj.AddPropertyF("id", "functions/tag-%" Px "", start()); | 355 obj.AddPropertyF("id", "functions/tag-%" Px "", start()); |
| 536 func.AddProperty("name", name()); | 356 func.AddProperty("name", name()); |
| 537 } | 357 } |
| 538 } | 358 } |
| 539 | 359 |
| 540 void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) { | 360 void PrintToJSONArray(Isolate* isolate, JSONArray* events) { |
| 541 JSONObject obj(events); | 361 JSONObject obj(events); |
| 542 obj.AddProperty("kind", KindToCString(kind())); | 362 obj.AddProperty("kind", KindToCString(kind())); |
| 543 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); | 363 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); |
| 544 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); | 364 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); |
| 545 if (kind() == kDartCode) { | 365 if (kind() == kDartCode) { |
| 546 // Look up code in Dart heap. | 366 // Look up code in Dart heap. |
| 547 Code& code = Code::Handle(isolate); | 367 Code& code = Code::Handle(isolate); |
| 548 code ^= Code::LookupCode(start()); | 368 code ^= Code::LookupCode(start()); |
| 549 if (code.IsNull()) { | 369 if (code.IsNull()) { |
| 550 // Code is a stub in the Vm isolate. | 370 // Code is a stub in the Vm isolate. |
| 551 code ^= Code::LookupCodeInVmIsolate(start()); | 371 code ^= Code::LookupCodeInVmIsolate(start()); |
| 552 } | 372 } |
| 553 ASSERT(!code.IsNull()); | 373 ASSERT(!code.IsNull()); |
| 554 obj.AddProperty("code", code, !full); | 374 obj.AddProperty("code", code); |
| 555 } else if (kind() == kCollectedCode) { | 375 } else if (kind() == kCollectedCode) { |
| 556 if (name() == NULL) { | 376 if (name() == NULL) { |
| 557 // Lazily set generated name. | 377 // Lazily set generated name. |
| 558 GenerateAndSetSymbolName("[Collected]"); | 378 GenerateAndSetSymbolName("[Collected]"); |
| 559 } | 379 } |
| 560 PrintCollectedCode(&obj); | 380 PrintCollectedCode(&obj); |
| 561 } else if (kind() == kReusedCode) { | 381 } else if (kind() == kReusedCode) { |
| 562 if (name() == NULL) { | 382 if (name() == NULL) { |
| 563 // Lazily set generated name. | 383 // Lazily set generated name. |
| 564 GenerateAndSetSymbolName("[Reused]"); | 384 GenerateAndSetSymbolName("[Reused]"); |
| (...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1218 dead_code_table_->Length(); | 1038 dead_code_table_->Length(); |
| 1219 intptr_t root_index = tag_code_table_->FindIndex(0); | 1039 intptr_t root_index = tag_code_table_->FindIndex(0); |
| 1220 // Verify that the "0" tag does not exist. | 1040 // Verify that the "0" tag does not exist. |
| 1221 ASSERT(root_index < 0); | 1041 ASSERT(root_index < 0); |
| 1222 // Insert the dummy tag CodeRegion that is used for the Trie root. | 1042 // Insert the dummy tag CodeRegion that is used for the Trie root. |
| 1223 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0); | 1043 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0); |
| 1224 root_index = tag_code_table_->InsertCodeRegion(region); | 1044 root_index = tag_code_table_->InsertCodeRegion(region); |
| 1225 ASSERT(root_index >= 0); | 1045 ASSERT(root_index >= 0); |
| 1226 region->set_creation_serial(0); | 1046 region->set_creation_serial(0); |
| 1227 root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index); | 1047 root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index); |
| 1228 set_tag_order(Profiler::kUserVM); | 1048 set_tag_order(ProfilerService::kUserVM); |
| 1229 } | 1049 } |
| 1230 | 1050 |
| 1231 void VisitSample(Sample* sample) { | 1051 void VisitSample(Sample* sample) { |
| 1232 // Give the root a tick. | 1052 // Give the root a tick. |
| 1233 root_->Tick(); | 1053 root_->Tick(); |
| 1234 CodeRegionTrieNode* current = root_; | 1054 CodeRegionTrieNode* current = root_; |
| 1235 current = ProcessTags(sample, current); | 1055 current = ProcessTags(sample, current); |
| 1236 // Walk the sampled PCs. | 1056 // Walk the sampled PCs. |
| 1237 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | 1057 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| 1238 if (sample->At(i) == 0) { | 1058 if (sample->At(i) == 0) { |
| 1239 break; | 1059 break; |
| 1240 } | 1060 } |
| 1241 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); | 1061 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
| 1242 current = current->GetChild(index); | 1062 current = current->GetChild(index); |
| 1243 current->Tick(); | 1063 current->Tick(); |
| 1244 } | 1064 } |
| 1245 } | 1065 } |
| 1246 | 1066 |
| 1247 CodeRegionTrieNode* root() const { | 1067 CodeRegionTrieNode* root() const { |
| 1248 return root_; | 1068 return root_; |
| 1249 } | 1069 } |
| 1250 | 1070 |
| 1251 Profiler::TagOrder tag_order() const { | 1071 ProfilerService::TagOrder tag_order() const { |
| 1252 return tag_order_; | 1072 return tag_order_; |
| 1253 } | 1073 } |
| 1254 | 1074 |
| 1255 void set_tag_order(Profiler::TagOrder tag_order) { | 1075 void set_tag_order(ProfilerService::TagOrder tag_order) { |
| 1256 tag_order_ = tag_order; | 1076 tag_order_ = tag_order; |
| 1257 } | 1077 } |
| 1258 | 1078 |
| 1259 private: | 1079 private: |
| 1260 CodeRegionTrieNode* ProcessUserTags(Sample* sample, | 1080 CodeRegionTrieNode* ProcessUserTags(Sample* sample, |
| 1261 CodeRegionTrieNode* current) { | 1081 CodeRegionTrieNode* current) { |
| 1262 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); | 1082 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
| 1263 if (user_tag_index >= 0) { | 1083 if (user_tag_index >= 0) { |
| 1264 current = current->GetChild(user_tag_index); | 1084 current = current->GetChild(user_tag_index); |
| 1265 // Give the tag a tick. | 1085 // Give the tag a tick. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1285 } | 1105 } |
| 1286 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1106 intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
| 1287 current = current->GetChild(tag_index); | 1107 current = current->GetChild(tag_index); |
| 1288 // Give the tag a tick. | 1108 // Give the tag a tick. |
| 1289 current->Tick(); | 1109 current->Tick(); |
| 1290 return current; | 1110 return current; |
| 1291 } | 1111 } |
| 1292 | 1112 |
| 1293 CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { | 1113 CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { |
| 1294 // None. | 1114 // None. |
| 1295 if (tag_order() == Profiler::kNoTags) { | 1115 if (tag_order() == ProfilerService::kNoTags) { |
| 1296 return current; | 1116 return current; |
| 1297 } | 1117 } |
| 1298 // User first. | 1118 // User first. |
| 1299 if ((tag_order() == Profiler::kUserVM) || | 1119 if ((tag_order() == ProfilerService::kUserVM) || |
| 1300 (tag_order() == Profiler::kUser)) { | 1120 (tag_order() == ProfilerService::kUser)) { |
| 1301 current = ProcessUserTags(sample, current); | 1121 current = ProcessUserTags(sample, current); |
| 1302 // Only user. | 1122 // Only user. |
| 1303 if (tag_order() == Profiler::kUser) { | 1123 if (tag_order() == ProfilerService::kUser) { |
| 1304 return current; | 1124 return current; |
| 1305 } | 1125 } |
| 1306 return ProcessVMTags(sample, current); | 1126 return ProcessVMTags(sample, current); |
| 1307 } | 1127 } |
| 1308 // VM first. | 1128 // VM first. |
| 1309 ASSERT((tag_order() == Profiler::kVMUser) || | 1129 ASSERT((tag_order() == ProfilerService::kVMUser) || |
| 1310 (tag_order() == Profiler::kVM)); | 1130 (tag_order() == ProfilerService::kVM)); |
| 1311 current = ProcessVMTags(sample, current); | 1131 current = ProcessVMTags(sample, current); |
| 1312 // Only VM. | 1132 // Only VM. |
| 1313 if (tag_order() == Profiler::kVM) { | 1133 if (tag_order() == ProfilerService::kVM) { |
| 1314 return current; | 1134 return current; |
| 1315 } | 1135 } |
| 1316 return ProcessUserTags(sample, current); | 1136 return ProcessUserTags(sample, current); |
| 1317 } | 1137 } |
| 1318 | 1138 |
| 1319 intptr_t FindTagIndex(uword tag) const { | 1139 intptr_t FindTagIndex(uword tag) const { |
| 1320 if (tag == 0) { | 1140 if (tag == 0) { |
| 1321 return -1; | 1141 return -1; |
| 1322 } | 1142 } |
| 1323 intptr_t index = tag_code_table_->FindIndex(tag); | 1143 intptr_t index = tag_code_table_->FindIndex(tag); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1340 ASSERT(index >= 0); | 1160 ASSERT(index >= 0); |
| 1341 region = dead_code_table_->At(index); | 1161 region = dead_code_table_->At(index); |
| 1342 ASSERT(region->contains(pc)); | 1162 ASSERT(region->contains(pc)); |
| 1343 ASSERT(region->compile_timestamp() <= timestamp); | 1163 ASSERT(region->compile_timestamp() <= timestamp); |
| 1344 return index + dead_code_table_offset_; | 1164 return index + dead_code_table_offset_; |
| 1345 } | 1165 } |
| 1346 ASSERT(region->compile_timestamp() <= timestamp); | 1166 ASSERT(region->compile_timestamp() <= timestamp); |
| 1347 return index; | 1167 return index; |
| 1348 } | 1168 } |
| 1349 | 1169 |
| 1350 Profiler::TagOrder tag_order_; | 1170 ProfilerService::TagOrder tag_order_; |
| 1351 CodeRegionTrieNode* root_; | 1171 CodeRegionTrieNode* root_; |
| 1352 CodeRegionTable* live_code_table_; | 1172 CodeRegionTable* live_code_table_; |
| 1353 CodeRegionTable* dead_code_table_; | 1173 CodeRegionTable* dead_code_table_; |
| 1354 CodeRegionTable* tag_code_table_; | 1174 CodeRegionTable* tag_code_table_; |
| 1355 intptr_t dead_code_table_offset_; | 1175 intptr_t dead_code_table_offset_; |
| 1356 intptr_t tag_code_table_offset_; | 1176 intptr_t tag_code_table_offset_; |
| 1357 }; | 1177 }; |
| 1358 | 1178 |
| 1359 | 1179 |
| 1360 class CodeRegionTableCallersBuilder { | 1180 class CodeRegionTableCallersBuilder { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1412 | 1232 |
| 1413 CodeRegionTrieNode* exclusive_root_; | 1233 CodeRegionTrieNode* exclusive_root_; |
| 1414 CodeRegionTable* live_code_table_; | 1234 CodeRegionTable* live_code_table_; |
| 1415 CodeRegionTable* dead_code_table_; | 1235 CodeRegionTable* dead_code_table_; |
| 1416 CodeRegionTable* tag_code_table_; | 1236 CodeRegionTable* tag_code_table_; |
| 1417 intptr_t dead_code_table_offset_; | 1237 intptr_t dead_code_table_offset_; |
| 1418 intptr_t tag_code_table_offset_; | 1238 intptr_t tag_code_table_offset_; |
| 1419 }; | 1239 }; |
| 1420 | 1240 |
| 1421 | 1241 |
| 1422 void Profiler::PrintJSON(Isolate* isolate, JSONStream* stream, | 1242 void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
| 1423 bool full, TagOrder tag_order) { | 1243 Isolate* isolate = Isolate::Current(); |
| 1424 ASSERT(isolate == Isolate::Current()); | |
| 1425 // Disable profile interrupts while processing the buffer. | 1244 // Disable profile interrupts while processing the buffer. |
| 1426 EndExecution(isolate); | 1245 Profiler::EndExecution(isolate); |
| 1427 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1246 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 1428 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1247 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 1429 if (profiler_data == NULL) { | 1248 if (profiler_data == NULL) { |
| 1430 JSONObject error(stream); | 1249 JSONObject error(stream); |
| 1431 error.AddProperty("type", "Error"); | 1250 error.AddProperty("type", "Error"); |
| 1432 error.AddProperty("text", "Isolate does not have profiling enabled."); | 1251 error.AddProperty("text", "Isolate does not have profiling enabled."); |
| 1433 return; | 1252 return; |
| 1434 } | 1253 } |
| 1435 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1254 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 1436 ASSERT(sample_buffer != NULL); | 1255 ASSERT(sample_buffer != NULL); |
| 1437 { | 1256 { |
| 1438 StackZone zone(isolate); | 1257 StackZone zone(isolate); |
| 1439 { | 1258 { |
| 1440 // Live code holds Dart, Native, and Collected CodeRegions. | 1259 // Live code holds Dart, Native, and Collected CodeRegions. |
| 1441 CodeRegionTable live_code_table; | 1260 CodeRegionTable live_code_table; |
| 1442 // Dead code holds Overwritten CodeRegions. | 1261 // Dead code holds Overwritten CodeRegions. |
| 1443 CodeRegionTable dead_code_table; | 1262 CodeRegionTable dead_code_table; |
| 1444 // Tag code holds Tag CodeRegions. | 1263 // Tag code holds Tag CodeRegions. |
| 1445 CodeRegionTable tag_code_table; | 1264 CodeRegionTable tag_code_table; |
| 1446 CodeRegionTableBuilder builder(isolate, | 1265 CodeRegionTableBuilder builder(isolate, |
| 1447 &live_code_table, | 1266 &live_code_table, |
| 1448 &dead_code_table, | 1267 &dead_code_table, |
| 1449 &tag_code_table); | 1268 &tag_code_table); |
| 1450 { | 1269 { |
| 1451 ScopeStopwatch sw("FixTopFrame"); | 1270 ScopeTimer sw("FixTopFrame", FLAG_trace_profiler); |
| 1452 // Preprocess samples and fix the caller when the top PC is in a | 1271 // Preprocess samples and fix the caller when the top PC is in a |
| 1453 // stub or intrinsic without a frame. | 1272 // stub or intrinsic without a frame. |
| 1454 FixTopFrameVisitor fixTopFrame(isolate); | 1273 FixTopFrameVisitor fixTopFrame(isolate); |
| 1455 sample_buffer->VisitSamples(&fixTopFrame); | 1274 sample_buffer->VisitSamples(&fixTopFrame); |
| 1456 } | 1275 } |
| 1457 { | 1276 { |
| 1458 // Build CodeRegion tables. | 1277 // Build CodeRegion tables. |
| 1459 ScopeStopwatch sw("CodeRegionTableBuilder"); | 1278 ScopeTimer sw("CodeRegionTableBuilder", FLAG_trace_profiler); |
| 1460 sample_buffer->VisitSamples(&builder); | 1279 sample_buffer->VisitSamples(&builder); |
| 1461 } | 1280 } |
| 1462 intptr_t samples = builder.visited(); | 1281 intptr_t samples = builder.visited(); |
| 1463 intptr_t frames = builder.frames(); | 1282 intptr_t frames = builder.frames(); |
| 1464 if (FLAG_trace_profiler) { | 1283 if (FLAG_trace_profiler) { |
| 1465 intptr_t total_live_code_objects = live_code_table.Length(); | 1284 intptr_t total_live_code_objects = live_code_table.Length(); |
| 1466 intptr_t total_dead_code_objects = dead_code_table.Length(); | 1285 intptr_t total_dead_code_objects = dead_code_table.Length(); |
| 1467 intptr_t total_tag_code_objects = tag_code_table.Length(); | 1286 intptr_t total_tag_code_objects = tag_code_table.Length(); |
| 1468 OS::Print("Processed %" Pd " frames\n", frames); | 1287 OS::Print("Processed %" Pd " frames\n", frames); |
| 1469 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", | 1288 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", |
| 1470 total_live_code_objects, | 1289 total_live_code_objects, |
| 1471 total_dead_code_objects, | 1290 total_dead_code_objects, |
| 1472 total_tag_code_objects); | 1291 total_tag_code_objects); |
| 1473 } | 1292 } |
| 1474 #if defined(DEBUG) | 1293 #if defined(DEBUG) |
| 1475 live_code_table.Verify(); | 1294 live_code_table.Verify(); |
| 1476 dead_code_table.Verify(); | 1295 dead_code_table.Verify(); |
| 1477 tag_code_table.Verify(); | 1296 tag_code_table.Verify(); |
| 1478 if (FLAG_trace_profiler) { | 1297 if (FLAG_trace_profiler) { |
| 1479 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); | 1298 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); |
| 1480 } | 1299 } |
| 1481 #endif | 1300 #endif |
| 1482 CodeRegionExclusiveTrieBuilder build_trie(isolate, | 1301 CodeRegionExclusiveTrieBuilder build_trie(isolate, |
| 1483 &live_code_table, | 1302 &live_code_table, |
| 1484 &dead_code_table, | 1303 &dead_code_table, |
| 1485 &tag_code_table); | 1304 &tag_code_table); |
| 1486 build_trie.set_tag_order(tag_order); | 1305 build_trie.set_tag_order(tag_order); |
| 1487 { | 1306 { |
| 1488 // Build CodeRegion trie. | 1307 // Build CodeRegion trie. |
| 1489 ScopeStopwatch sw("CodeRegionExclusiveTrieBuilder"); | 1308 ScopeTimer sw("CodeRegionExclusiveTrieBuilder", FLAG_trace_profiler); |
| 1490 sample_buffer->VisitSamples(&build_trie); | 1309 sample_buffer->VisitSamples(&build_trie); |
| 1491 build_trie.root()->SortByCount(); | 1310 build_trie.root()->SortByCount(); |
| 1492 } | 1311 } |
| 1493 CodeRegionTableCallersBuilder build_callers(build_trie.root(), | 1312 CodeRegionTableCallersBuilder build_callers(build_trie.root(), |
| 1494 &live_code_table, | 1313 &live_code_table, |
| 1495 &dead_code_table, | 1314 &dead_code_table, |
| 1496 &tag_code_table); | 1315 &tag_code_table); |
| 1497 { | 1316 { |
| 1498 // Build CodeRegion callers. | 1317 // Build CodeRegion callers. |
| 1499 ScopeStopwatch sw("CodeRegionTableCallersBuilder"); | 1318 ScopeTimer sw("CodeRegionTableCallersBuilder", FLAG_trace_profiler); |
| 1500 build_callers.Build(); | 1319 build_callers.Build(); |
| 1501 } | 1320 } |
| 1502 { | 1321 { |
| 1503 ScopeStopwatch sw("CodeTableStream"); | 1322 ScopeTimer sw("CodeTableStream", FLAG_trace_profiler); |
| 1504 // Serialize to JSON. | 1323 // Serialize to JSON. |
| 1505 JSONObject obj(stream); | 1324 JSONObject obj(stream); |
| 1506 obj.AddProperty("type", "CpuProfile"); | 1325 obj.AddProperty("type", "CpuProfile"); |
| 1507 obj.AddProperty("id", "profile"); | 1326 obj.AddProperty("id", "profile"); |
| 1508 obj.AddProperty("samples", samples); | 1327 obj.AddProperty("samples", samples); |
| 1509 obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth)); | 1328 obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth)); |
| 1510 obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period)); | 1329 obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period)); |
| 1511 obj.AddProperty("timeSpan", | 1330 obj.AddProperty("timeSpan", |
| 1512 MicrosecondsToSeconds(builder.TimeDeltaMicros())); | 1331 MicrosecondsToSeconds(builder.TimeDeltaMicros())); |
| 1513 { | 1332 { |
| 1514 JSONArray exclusive_trie(&obj, "exclusive_trie"); | 1333 JSONArray exclusive_trie(&obj, "exclusive_trie"); |
| 1515 CodeRegionTrieNode* root = build_trie.root(); | 1334 CodeRegionTrieNode* root = build_trie.root(); |
| 1516 ASSERT(root != NULL); | 1335 ASSERT(root != NULL); |
| 1517 root->PrintToJSONArray(&exclusive_trie); | 1336 root->PrintToJSONArray(&exclusive_trie); |
| 1518 } | 1337 } |
| 1519 JSONArray codes(&obj, "codes"); | 1338 JSONArray codes(&obj, "codes"); |
| 1520 for (intptr_t i = 0; i < live_code_table.Length(); i++) { | 1339 for (intptr_t i = 0; i < live_code_table.Length(); i++) { |
| 1521 CodeRegion* region = live_code_table.At(i); | 1340 CodeRegion* region = live_code_table.At(i); |
| 1522 ASSERT(region != NULL); | 1341 ASSERT(region != NULL); |
| 1523 region->PrintToJSONArray(isolate, &codes, full); | 1342 region->PrintToJSONArray(isolate, &codes); |
| 1524 } | 1343 } |
| 1525 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { | 1344 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { |
| 1526 CodeRegion* region = dead_code_table.At(i); | 1345 CodeRegion* region = dead_code_table.At(i); |
| 1527 ASSERT(region != NULL); | 1346 ASSERT(region != NULL); |
| 1528 region->PrintToJSONArray(isolate, &codes, full); | 1347 region->PrintToJSONArray(isolate, &codes); |
| 1529 } | 1348 } |
| 1530 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { | 1349 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { |
| 1531 CodeRegion* region = tag_code_table.At(i); | 1350 CodeRegion* region = tag_code_table.At(i); |
| 1532 ASSERT(region != NULL); | 1351 ASSERT(region != NULL); |
| 1533 region->PrintToJSONArray(isolate, &codes, full); | 1352 region->PrintToJSONArray(isolate, &codes); |
| 1534 } | 1353 } |
| 1535 } | 1354 } |
| 1536 } | 1355 } |
| 1537 } | 1356 } |
| 1538 // Enable profile interrupts. | 1357 // Enable profile interrupts. |
| 1539 BeginExecution(isolate); | 1358 Profiler::BeginExecution(isolate); |
| 1540 } | |
| 1541 | |
| 1542 | |
| 1543 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, | |
| 1544 bool own_sample_buffer) { | |
| 1545 ASSERT(sample_buffer != NULL); | |
| 1546 sample_buffer_ = sample_buffer; | |
| 1547 own_sample_buffer_ = own_sample_buffer; | |
| 1548 block_count_ = 0; | |
| 1549 } | |
| 1550 | |
| 1551 | |
| 1552 IsolateProfilerData::~IsolateProfilerData() { | |
| 1553 if (own_sample_buffer_) { | |
| 1554 delete sample_buffer_; | |
| 1555 sample_buffer_ = NULL; | |
| 1556 own_sample_buffer_ = false; | |
| 1557 } | |
| 1558 } | |
| 1559 | |
| 1560 | |
| 1561 void IsolateProfilerData::Block() { | |
| 1562 block_count_++; | |
| 1563 } | |
| 1564 | |
| 1565 | |
| 1566 void IsolateProfilerData::Unblock() { | |
| 1567 block_count_--; | |
| 1568 if (block_count_ < 0) { | |
| 1569 FATAL("Too many calls to Dart_IsolateUnblocked."); | |
| 1570 } | |
| 1571 if (!blocked()) { | |
| 1572 // We just unblocked this isolate, wake up the thread interrupter. | |
| 1573 ThreadInterrupter::WakeUp(); | |
| 1574 } | |
| 1575 } | |
| 1576 | |
| 1577 | |
| 1578 intptr_t Sample::pcs_length_ = 0; | |
| 1579 intptr_t Sample::instance_size_ = 0; | |
| 1580 | |
| 1581 | |
| 1582 void Sample::InitOnce() { | |
| 1583 ASSERT(FLAG_profile_depth >= 1); | |
| 1584 pcs_length_ = FLAG_profile_depth; | |
| 1585 instance_size_ = | |
| 1586 sizeof(Sample) + (sizeof(uword) * pcs_length_); // NOLINT. | |
| 1587 } | |
| 1588 | |
| 1589 | |
| 1590 uword* Sample::GetPCArray() const { | |
| 1591 return reinterpret_cast<uword*>( | |
| 1592 reinterpret_cast<uintptr_t>(this) + sizeof(*this)); | |
| 1593 } | |
| 1594 | |
| 1595 | |
| 1596 SampleBuffer::SampleBuffer(intptr_t capacity) { | |
| 1597 ASSERT(Sample::instance_size() > 0); | |
| 1598 samples_ = reinterpret_cast<Sample*>( | |
| 1599 calloc(capacity, Sample::instance_size())); | |
| 1600 capacity_ = capacity; | |
| 1601 cursor_ = 0; | |
| 1602 } | |
| 1603 | |
| 1604 | |
| 1605 Sample* SampleBuffer::At(intptr_t idx) const { | |
| 1606 ASSERT(idx >= 0); | |
| 1607 ASSERT(idx < capacity_); | |
| 1608 intptr_t offset = idx * Sample::instance_size(); | |
| 1609 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_); | |
| 1610 return reinterpret_cast<Sample*>(samples + offset); | |
| 1611 } | |
| 1612 | |
| 1613 | |
| 1614 Sample* SampleBuffer::ReserveSample() { | |
| 1615 ASSERT(samples_ != NULL); | |
| 1616 uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_); | |
| 1617 // Map back into sample buffer range. | |
| 1618 cursor = cursor % capacity_; | |
| 1619 return At(cursor); | |
| 1620 } | |
| 1621 | |
| 1622 | |
| 1623 static void SetPCMarkerIfSafe(Sample* sample) { | |
| 1624 ASSERT(sample != NULL); | |
| 1625 | |
| 1626 uword* fp = reinterpret_cast<uword*>(sample->fp()); | |
| 1627 uword* sp = reinterpret_cast<uword*>(sample->sp()); | |
| 1628 | |
| 1629 // If FP == SP, the pc marker hasn't been pushed. | |
| 1630 if (fp > sp) { | |
| 1631 #if defined(TARGET_OS_WINDOWS) | |
| 1632 // If the fp is at the beginning of a page, it may be unsafe to access | |
| 1633 // the pc marker, because we are reading it from a different thread on | |
| 1634 // Windows. The marker is below fp and the previous page may be a guard | |
| 1635 // page. | |
| 1636 const intptr_t kPageMask = VirtualMemory::PageSize() - 1; | |
| 1637 if ((sample->fp() & kPageMask) == 0) { | |
| 1638 return; | |
| 1639 } | |
| 1640 #endif | |
| 1641 uword* pc_marker_ptr = fp + kPcMarkerSlotFromFp; | |
| 1642 // MSan/ASan are unaware of frames initialized by generated code. | |
| 1643 MSAN_UNPOISON(pc_marker_ptr, kWordSize); | |
| 1644 ASAN_UNPOISON(pc_marker_ptr, kWordSize); | |
| 1645 sample->set_pc_marker(*pc_marker_ptr); | |
| 1646 } | |
| 1647 } | |
| 1648 | |
| 1649 | |
| 1650 // Given an exit frame, walk the Dart stack. | |
| 1651 class ProfilerDartExitStackWalker : public ValueObject { | |
| 1652 public: | |
| 1653 ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample) | |
| 1654 : sample_(sample), | |
| 1655 frame_iterator_(isolate) { | |
| 1656 ASSERT(sample_ != NULL); | |
| 1657 // Mark that this sample was collected from an exit frame. | |
| 1658 sample_->set_exit_frame_sample(true); | |
| 1659 } | |
| 1660 | |
| 1661 void walk() { | |
| 1662 intptr_t frame_index = 0; | |
| 1663 StackFrame* frame = frame_iterator_.NextFrame(); | |
| 1664 while (frame != NULL) { | |
| 1665 sample_->SetAt(frame_index, frame->pc()); | |
| 1666 frame_index++; | |
| 1667 if (frame_index >= FLAG_profile_depth) { | |
| 1668 break; | |
| 1669 } | |
| 1670 frame = frame_iterator_.NextFrame(); | |
| 1671 } | |
| 1672 } | |
| 1673 | |
| 1674 private: | |
| 1675 Sample* sample_; | |
| 1676 DartFrameIterator frame_iterator_; | |
| 1677 }; | |
| 1678 | |
| 1679 | |
| 1680 // Executing Dart code, walk the stack. | |
| 1681 class ProfilerDartStackWalker : public ValueObject { | |
| 1682 public: | |
| 1683 ProfilerDartStackWalker(Isolate* isolate, | |
| 1684 Sample* sample, | |
| 1685 uword stack_lower, | |
| 1686 uword stack_upper, | |
| 1687 uword pc, | |
| 1688 uword fp, | |
| 1689 uword sp) | |
| 1690 : isolate_(isolate), | |
| 1691 sample_(sample), | |
| 1692 stack_upper_(stack_upper), | |
| 1693 stack_lower_(stack_lower) { | |
| 1694 ASSERT(sample_ != NULL); | |
| 1695 pc_ = reinterpret_cast<uword*>(pc); | |
| 1696 fp_ = reinterpret_cast<uword*>(fp); | |
| 1697 sp_ = reinterpret_cast<uword*>(sp); | |
| 1698 } | |
| 1699 | |
| 1700 void walk() { | |
| 1701 if (!ValidFramePointer()) { | |
| 1702 sample_->set_ignore_sample(true); | |
| 1703 return; | |
| 1704 } | |
| 1705 ASSERT(ValidFramePointer()); | |
| 1706 uword return_pc = InitialReturnAddress(); | |
| 1707 if (StubCode::InInvocationStubForIsolate(isolate_, return_pc)) { | |
| 1708 // Edge case- we have called out from the Invocation Stub but have not | |
| 1709 // created the stack frame of the callee. Attempt to locate the exit | |
| 1710 // frame before walking the stack. | |
| 1711 if (!NextExit() || !ValidFramePointer()) { | |
| 1712 // Nothing to sample. | |
| 1713 sample_->set_ignore_sample(true); | |
| 1714 return; | |
| 1715 } | |
| 1716 } | |
| 1717 for (int i = 0; i < FLAG_profile_depth; i++) { | |
| 1718 sample_->SetAt(i, reinterpret_cast<uword>(pc_)); | |
| 1719 if (!Next()) { | |
| 1720 return; | |
| 1721 } | |
| 1722 } | |
| 1723 } | |
| 1724 | |
| 1725 private: | |
| 1726 bool Next() { | |
| 1727 if (!ValidFramePointer()) { | |
| 1728 return false; | |
| 1729 } | |
| 1730 if (StubCode::InInvocationStubForIsolate(isolate_, | |
| 1731 reinterpret_cast<uword>(pc_))) { | |
| 1732 // In invocation stub. | |
| 1733 return NextExit(); | |
| 1734 } | |
| 1735 // In regular Dart frame. | |
| 1736 uword* new_pc = CallerPC(); | |
| 1737 // Check if we've moved into the invocation stub. | |
| 1738 if (StubCode::InInvocationStubForIsolate(isolate_, | |
| 1739 reinterpret_cast<uword>(new_pc))) { | |
| 1740 // New PC is inside invocation stub, skip. | |
| 1741 return NextExit(); | |
| 1742 } | |
| 1743 uword* new_fp = CallerFP(); | |
| 1744 if (new_fp <= fp_) { | |
| 1745 // FP didn't move to a higher address. | |
| 1746 return false; | |
| 1747 } | |
| 1748 // Success, update fp and pc. | |
| 1749 fp_ = new_fp; | |
| 1750 pc_ = new_pc; | |
| 1751 return true; | |
| 1752 } | |
| 1753 | |
| 1754 bool NextExit() { | |
| 1755 if (!ValidFramePointer()) { | |
| 1756 return false; | |
| 1757 } | |
| 1758 uword* new_fp = ExitLink(); | |
| 1759 if (new_fp == NULL) { | |
| 1760 // No exit link. | |
| 1761 return false; | |
| 1762 } | |
| 1763 if (new_fp <= fp_) { | |
| 1764 // FP didn't move to a higher address. | |
| 1765 return false; | |
| 1766 } | |
| 1767 if (!ValidFramePointer(new_fp)) { | |
| 1768 return false; | |
| 1769 } | |
| 1770 // Success, update fp and pc. | |
| 1771 fp_ = new_fp; | |
| 1772 pc_ = CallerPC(); | |
| 1773 return true; | |
| 1774 } | |
| 1775 | |
| 1776 uword InitialReturnAddress() const { | |
| 1777 ASSERT(sp_ != NULL); | |
| 1778 return *(sp_); | |
| 1779 } | |
| 1780 | |
| 1781 uword* CallerPC() const { | |
| 1782 ASSERT(fp_ != NULL); | |
| 1783 return reinterpret_cast<uword*>(*(fp_ + kSavedCallerPcSlotFromFp)); | |
| 1784 } | |
| 1785 | |
| 1786 uword* CallerFP() const { | |
| 1787 ASSERT(fp_ != NULL); | |
| 1788 return reinterpret_cast<uword*>(*(fp_ + kSavedCallerFpSlotFromFp)); | |
| 1789 } | |
| 1790 | |
| 1791 uword* ExitLink() const { | |
| 1792 ASSERT(fp_ != NULL); | |
| 1793 return reinterpret_cast<uword*>(*(fp_ + kExitLinkSlotFromEntryFp)); | |
| 1794 } | |
| 1795 | |
| 1796 bool ValidFramePointer() const { | |
| 1797 return ValidFramePointer(fp_); | |
| 1798 } | |
| 1799 | |
| 1800 bool ValidFramePointer(uword* fp) const { | |
| 1801 if (fp == NULL) { | |
| 1802 return false; | |
| 1803 } | |
| 1804 uword cursor = reinterpret_cast<uword>(fp); | |
| 1805 cursor += sizeof(fp); | |
| 1806 return (cursor >= stack_lower_) && (cursor < stack_upper_); | |
| 1807 } | |
| 1808 | |
| 1809 uword* pc_; | |
| 1810 uword* fp_; | |
| 1811 uword* sp_; | |
| 1812 Isolate* isolate_; | |
| 1813 Sample* sample_; | |
| 1814 const uword stack_upper_; | |
| 1815 uword stack_lower_; | |
| 1816 }; | |
| 1817 | |
| 1818 | |
| 1819 // If the VM is compiled without frame pointers (which is the default on | |
| 1820 // recent GCC versions with optimizing enabled) the stack walking code may | |
| 1821 // fail. | |
| 1822 // | |
| 1823 class ProfilerNativeStackWalker : public ValueObject { | |
| 1824 public: | |
| 1825 ProfilerNativeStackWalker(Sample* sample, | |
| 1826 uword stack_lower, | |
| 1827 uword stack_upper, | |
| 1828 uword pc, | |
| 1829 uword fp, | |
| 1830 uword sp) | |
| 1831 : sample_(sample), | |
| 1832 stack_upper_(stack_upper), | |
| 1833 original_pc_(pc), | |
| 1834 original_fp_(fp), | |
| 1835 original_sp_(sp), | |
| 1836 lower_bound_(stack_lower) { | |
| 1837 ASSERT(sample_ != NULL); | |
| 1838 } | |
| 1839 | |
| 1840 void walk() { | |
| 1841 const uword kMaxStep = VirtualMemory::PageSize(); | |
| 1842 | |
| 1843 sample_->SetAt(0, original_pc_); | |
| 1844 | |
| 1845 uword* pc = reinterpret_cast<uword*>(original_pc_); | |
| 1846 uword* fp = reinterpret_cast<uword*>(original_fp_); | |
| 1847 uword* previous_fp = fp; | |
| 1848 | |
| 1849 uword gap = original_fp_ - original_sp_; | |
| 1850 if (gap >= kMaxStep) { | |
| 1851 // Gap between frame pointer and stack pointer is | |
| 1852 // too large. | |
| 1853 return; | |
| 1854 } | |
| 1855 | |
| 1856 if (!ValidFramePointer(fp)) { | |
| 1857 return; | |
| 1858 } | |
| 1859 | |
| 1860 for (int i = 0; i < FLAG_profile_depth; i++) { | |
| 1861 sample_->SetAt(i, reinterpret_cast<uword>(pc)); | |
| 1862 | |
| 1863 pc = CallerPC(fp); | |
| 1864 previous_fp = fp; | |
| 1865 fp = CallerFP(fp); | |
| 1866 | |
| 1867 if (fp == NULL) { | |
| 1868 return; | |
| 1869 } | |
| 1870 | |
| 1871 if (fp <= previous_fp) { | |
| 1872 // Frame pointer did not move to a higher address. | |
| 1873 return; | |
| 1874 } | |
| 1875 | |
| 1876 gap = fp - previous_fp; | |
| 1877 if (gap >= kMaxStep) { | |
| 1878 // Frame pointer step is too large. | |
| 1879 return; | |
| 1880 } | |
| 1881 | |
| 1882 if (!ValidFramePointer(fp)) { | |
| 1883 // Frame pointer is outside of isolate stack boundary. | |
| 1884 return; | |
| 1885 } | |
| 1886 | |
| 1887 // Move the lower bound up. | |
| 1888 lower_bound_ = reinterpret_cast<uword>(fp); | |
| 1889 } | |
| 1890 } | |
| 1891 | |
| 1892 private: | |
| 1893 uword* CallerPC(uword* fp) const { | |
| 1894 ASSERT(fp != NULL); | |
| 1895 uword* caller_pc_ptr = fp + kSavedCallerPcSlotFromFp; | |
| 1896 // This may actually be uninitialized, by design (see class comment above). | |
| 1897 MSAN_UNPOISON(caller_pc_ptr, kWordSize); | |
| 1898 ASAN_UNPOISON(caller_pc_ptr, kWordSize); | |
| 1899 return reinterpret_cast<uword*>(*caller_pc_ptr); | |
| 1900 } | |
| 1901 | |
| 1902 uword* CallerFP(uword* fp) const { | |
| 1903 ASSERT(fp != NULL); | |
| 1904 uword* caller_fp_ptr = fp + kSavedCallerFpSlotFromFp; | |
| 1905 // This may actually be uninitialized, by design (see class comment above). | |
| 1906 MSAN_UNPOISON(caller_fp_ptr, kWordSize); | |
| 1907 ASAN_UNPOISON(caller_fp_ptr, kWordSize); | |
| 1908 return reinterpret_cast<uword*>(*caller_fp_ptr); | |
| 1909 } | |
| 1910 | |
| 1911 bool ValidFramePointer(uword* fp) const { | |
| 1912 if (fp == NULL) { | |
| 1913 return false; | |
| 1914 } | |
| 1915 uword cursor = reinterpret_cast<uword>(fp); | |
| 1916 cursor += sizeof(fp); | |
| 1917 bool r = (cursor >= lower_bound_) && (cursor < stack_upper_); | |
| 1918 return r; | |
| 1919 } | |
| 1920 | |
| 1921 Sample* sample_; | |
| 1922 const uword stack_upper_; | |
| 1923 const uword original_pc_; | |
| 1924 const uword original_fp_; | |
| 1925 const uword original_sp_; | |
| 1926 uword lower_bound_; | |
| 1927 }; | |
| 1928 | |
| 1929 | |
| 1930 void Profiler::RecordSampleInterruptCallback( | |
| 1931 const InterruptedThreadState& state, | |
| 1932 void* data) { | |
| 1933 Isolate* isolate = reinterpret_cast<Isolate*>(data); | |
| 1934 if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) { | |
| 1935 // No isolate. | |
| 1936 return; | |
| 1937 } | |
| 1938 | |
| 1939 ASSERT(isolate != Dart::vm_isolate()); | |
| 1940 | |
| 1941 uintptr_t sp = 0; | |
| 1942 if ((isolate->stub_code() != NULL) && | |
| 1943 (isolate->top_exit_frame_info() == 0) && | |
| 1944 (isolate->vm_tag() == VMTag::kDartTagId)) { | |
| 1945 // If we're in Dart code, use the Dart stack pointer. | |
| 1946 sp = state.dsp; | |
| 1947 } else { | |
| 1948 // If we're in runtime code, use the C stack pointer. | |
| 1949 sp = state.csp; | |
| 1950 } | |
| 1951 | |
| 1952 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 1953 if (profiler_data == NULL) { | |
| 1954 // Profiler not initialized. | |
| 1955 return; | |
| 1956 } | |
| 1957 | |
| 1958 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
| 1959 if (sample_buffer == NULL) { | |
| 1960 // Profiler not initialized. | |
| 1961 return; | |
| 1962 } | |
| 1963 | |
| 1964 if ((sp == 0) || (state.fp == 0) || (state.pc == 0)) { | |
| 1965 // None of these registers should be zero. | |
| 1966 return; | |
| 1967 } | |
| 1968 | |
| 1969 if (sp > state.fp) { | |
| 1970 // Assuming the stack grows down, we should never have a stack pointer above | |
| 1971 // the frame pointer. | |
| 1972 return; | |
| 1973 } | |
| 1974 | |
| 1975 if (StubCode::InJumpToExceptionHandlerStub(state.pc)) { | |
| 1976 // The JumpToExceptionHandler stub manually adjusts the stack pointer, | |
| 1977 // frame pointer, and some isolate state before jumping to a catch entry. | |
| 1978 // It is not safe to walk the stack when executing this stub. | |
| 1979 return; | |
| 1980 } | |
| 1981 | |
| 1982 uword stack_lower = 0; | |
| 1983 uword stack_upper = 0; | |
| 1984 if (!isolate->GetProfilerStackBounds(&stack_lower, &stack_upper) || | |
| 1985 (stack_lower == 0) || (stack_upper == 0)) { | |
| 1986 // Could not get stack boundary. | |
| 1987 return; | |
| 1988 } | |
| 1989 | |
| 1990 if (sp > stack_lower) { | |
| 1991 // The stack pointer gives us a tighter lower bound. | |
| 1992 stack_lower = sp; | |
| 1993 } | |
| 1994 | |
| 1995 if (stack_lower >= stack_upper) { | |
| 1996 // Stack boundary is invalid. | |
| 1997 return; | |
| 1998 } | |
| 1999 | |
| 2000 if ((sp < stack_lower) || (sp >= stack_upper)) { | |
| 2001 // Stack pointer is outside isolate stack boundary. | |
| 2002 return; | |
| 2003 } | |
| 2004 | |
| 2005 if ((state.fp < stack_lower) || (state.fp >= stack_upper)) { | |
| 2006 // Frame pointer is outside isolate stack boundary. | |
| 2007 return; | |
| 2008 } | |
| 2009 | |
| 2010 // At this point we have a valid stack boundary for this isolate and | |
| 2011 // know that our initial stack and frame pointers are within the boundary. | |
| 2012 | |
| 2013 // Setup sample. | |
| 2014 Sample* sample = sample_buffer->ReserveSample(); | |
| 2015 sample->Init(isolate, OS::GetCurrentTimeMicros(), state.tid); | |
| 2016 uword vm_tag = isolate->vm_tag(); | |
| 2017 #if defined(USING_SIMULATOR) | |
| 2018 // When running in the simulator, the runtime entry function address | |
| 2019 // (stored as the vm tag) is the address of a redirect function. | |
| 2020 // Attempt to find the real runtime entry function address and use that. | |
| 2021 uword redirect_vm_tag = Simulator::FunctionForRedirect(vm_tag); | |
| 2022 if (redirect_vm_tag != 0) { | |
| 2023 vm_tag = redirect_vm_tag; | |
| 2024 } | |
| 2025 #endif | |
| 2026 // Increment counter for vm tag. | |
| 2027 VMTagCounters* counters = isolate->vm_tag_counters(); | |
| 2028 ASSERT(counters != NULL); | |
| 2029 counters->Increment(vm_tag); | |
| 2030 sample->set_vm_tag(vm_tag); | |
| 2031 sample->set_user_tag(isolate->user_tag()); | |
| 2032 sample->set_sp(sp); | |
| 2033 sample->set_fp(state.fp); | |
| 2034 #if !(defined(TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64)) | |
| 2035 // It is never safe to read other thread's stack unless on Win64 | |
| 2036 // other thread is inside Dart code. | |
| 2037 SetPCMarkerIfSafe(sample); | |
| 2038 #endif | |
| 2039 | |
| 2040 // Walk the call stack. | |
| 2041 if (FLAG_profile_vm) { | |
| 2042 // Always walk the native stack collecting both native and Dart frames. | |
| 2043 ProfilerNativeStackWalker stackWalker(sample, | |
| 2044 stack_lower, | |
| 2045 stack_upper, | |
| 2046 state.pc, | |
| 2047 state.fp, | |
| 2048 sp); | |
| 2049 stackWalker.walk(); | |
| 2050 } else { | |
| 2051 // Attempt to walk only the Dart call stack, falling back to walking | |
| 2052 // the native stack. | |
| 2053 if ((isolate->stub_code() != NULL) && | |
| 2054 (isolate->top_exit_frame_info() != 0) && | |
| 2055 (isolate->vm_tag() != VMTag::kDartTagId)) { | |
| 2056 // We have a valid exit frame info, use the Dart stack walker. | |
| 2057 ProfilerDartExitStackWalker stackWalker(isolate, sample); | |
| 2058 stackWalker.walk(); | |
| 2059 } else if ((isolate->stub_code() != NULL) && | |
| 2060 (isolate->top_exit_frame_info() == 0) && | |
| 2061 (isolate->vm_tag() == VMTag::kDartTagId)) { | |
| 2062 // We are executing Dart code. We have frame pointers. | |
| 2063 ProfilerDartStackWalker stackWalker(isolate, | |
| 2064 sample, | |
| 2065 stack_lower, | |
| 2066 stack_upper, | |
| 2067 state.pc, | |
| 2068 state.fp, | |
| 2069 sp); | |
| 2070 stackWalker.walk(); | |
| 2071 } else { | |
| 2072 #if defined(TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64) | |
| 2073 // ProfilerNativeStackWalker is known to cause crashes on Win64. | |
| 2074 // BUG=20423. | |
| 2075 sample->set_ignore_sample(true); | |
| 2076 #else | |
| 2077 // Fall back to an extremely conservative stack walker. | |
| 2078 ProfilerNativeStackWalker stackWalker(sample, | |
| 2079 stack_lower, | |
| 2080 stack_upper, | |
| 2081 state.pc, | |
| 2082 state.fp, | |
| 2083 sp); | |
| 2084 stackWalker.walk(); | |
| 2085 #endif | |
| 2086 } | |
| 2087 } | |
| 2088 } | 1359 } |
| 2089 | 1360 |
| 2090 } // namespace dart | 1361 } // namespace dart |
| OLD | NEW |