OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 #include <cstdio> |
| 6 |
| 7 #include "platform/utils.h" |
| 8 |
| 9 #include "vm/isolate.h" |
| 10 #include "vm/json_stream.h" |
| 11 #include "vm/native_symbol.h" |
| 12 #include "vm/object.h" |
| 13 #include "vm/os.h" |
| 14 #include "vm/profiler.h" |
| 15 |
| 16 namespace dart { |
| 17 |
| 18 // Notes on locking and signal handling: |
| 19 |
| 20 // The ProfilerManager has a single monitor (monitor_). This monitor guards |
| 21 // access to the schedule list of isolates (isolates_, isolates_size_, etc). |
| 22 // |
| 23 // Each isolate has a mutex (profiler_data_mutex_) which protects access |
| 24 // to the isolate's profiler data. |
| 25 // |
| 26 // Locks can be taken in this order: |
| 27 // 1. ProfilerManager::monitor_ |
| 28 // 2. isolate->profiler_data_mutex_ |
| 29 // In other words, it is not acceptable to take ProfilerManager::monitor_ |
| 30 // after grabbing isolate->profiler_data_mutex_. |
| 31 // |
| 32 // ProfileManager::monitor_ taking entry points: |
| 33 // InitOnce, Shutdown |
| 34 // ProfilerManager::monitor_ |
| 35 // ScheduleIsolate, DescheduleIsolate. |
| 36 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| 37 // ThreadMain |
| 38 // isolate->profiler_data_mutex_ taking entry points: |
| 39 // SetupIsolateForProfiling, FreeIsolateForProfiling. |
| 40 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| 41 // ScheduleIsolate, DescheduleIsolate. |
| 42 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| 43 // ProfileSignalAction |
| 44 // isolate->profiler_data_mutex_ |
| 45 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ |
| 46 // |
| 47 // Signal handling and locking: |
| 48 // On OSes with pthreads (Android, Linux, and Mac) we use signal delivery |
| 49 // to interrupt the isolate running thread for sampling. After a thread |
| 50 // is sent the SIGPROF, it is removed from the scheduled isolate list. |
| 51 // Inside the signal handler, after the sample is taken, the isolate is |
| 52 // added to the scheduled isolate list again. The side effect of this is |
| 53 // that the signal handler must be able to acquire the isolate profiler data |
| 54 // mutex and the profile manager monitor. When an isolate running thread |
| 55 // (potential signal target) calls into an entry point which acquires |
| 56 // ProfileManager::monitor_ signal delivery must be blocked. An example is |
| 57 // Isolate::SetCurrent which blocks signal delivery while removing the old |
| 58 // current isolate from the scheduled list and adding the new current isolate |
| 59 // to the scheduled list. |
| 60 |
| 61 |
| 62 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); |
| 63 |
| 64 bool ProfilerManager::initialized_ = false; |
| 65 bool ProfilerManager::shutdown_ = false; |
| 66 Monitor* ProfilerManager::monitor_ = NULL; |
| 67 Isolate** ProfilerManager::isolates_ = NULL; |
| 68 intptr_t ProfilerManager::isolates_capacity_ = 0; |
| 69 intptr_t ProfilerManager::isolates_size_ = 0; |
| 70 |
| 71 |
| 72 void ProfilerManager::InitOnce() { |
| 73 if (!FLAG_profile) { |
| 74 return; |
| 75 } |
| 76 NativeSymbolResolver::InitOnce(); |
| 77 ASSERT(!initialized_); |
| 78 monitor_ = new Monitor(); |
| 79 initialized_ = true; |
| 80 ResizeIsolates(16); |
| 81 Thread::Start(ThreadMain, 0); |
| 82 } |
| 83 |
| 84 |
| 85 void ProfilerManager::Shutdown() { |
| 86 if (!FLAG_profile) { |
| 87 return; |
| 88 } |
| 89 ScopedMonitor lock(monitor_); |
| 90 shutdown_ = true; |
| 91 for (intptr_t i = 0; i < isolates_size_; i++) { |
| 92 Isolate* isolate = isolates_[i]; |
| 93 ASSERT(isolate != NULL); |
| 94 FreeIsolateProfilingData(isolate); |
| 95 } |
| 96 isolates_size_ = 0; |
| 97 free(isolates_); |
| 98 isolates_ = NULL; |
| 99 lock.Notify(); |
| 100 NativeSymbolResolver::ShutdownOnce(); |
| 101 } |
| 102 |
| 103 |
| 104 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { |
| 105 if (!FLAG_profile) { |
| 106 return; |
| 107 } |
| 108 ASSERT(isolate != NULL); |
| 109 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| 110 SampleBuffer* sample_buffer = new SampleBuffer(); |
| 111 IsolateProfilerData* profiler_data = |
| 112 new IsolateProfilerData(isolate, sample_buffer); |
| 113 profiler_data->set_sample_interval_micros(1000); |
| 114 isolate->set_profiler_data(profiler_data); |
| 115 } |
| 116 |
| 117 |
| 118 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { |
| 119 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| 120 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 121 if (profiler_data == NULL) { |
| 122 // Already freed. |
| 123 return; |
| 124 } |
| 125 isolate->set_profiler_data(NULL); |
| 126 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 127 ASSERT(sample_buffer != NULL); |
| 128 delete sample_buffer; |
| 129 delete profiler_data; |
| 130 } |
| 131 |
| 132 |
| 133 void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) { |
| 134 ASSERT(isolate != NULL); |
| 135 if (!FLAG_profile) { |
| 136 return; |
| 137 } |
| 138 FreeIsolateProfilingData(isolate); |
| 139 } |
| 140 |
| 141 |
| 142 void ProfilerManager::ScheduleIsolate(Isolate* isolate) { |
| 143 if (!FLAG_profile) { |
| 144 return; |
| 145 } |
| 146 ASSERT(initialized_); |
| 147 ASSERT(isolate != NULL); |
| 148 ScopedMonitor lock(monitor_); |
| 149 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| 150 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 151 if (profiler_data == NULL) { |
| 152 return; |
| 153 } |
| 154 profiler_data->Scheduled(OS::GetCurrentTimeMicros(), |
| 155 Thread::GetCurrentThreadId()); |
| 156 AddIsolate(isolate); |
| 157 lock.Notify(); |
| 158 } |
| 159 |
| 160 |
| 161 void ProfilerManager::DescheduleIsolate(Isolate* isolate) { |
| 162 if (!FLAG_profile) { |
| 163 return; |
| 164 } |
| 165 ASSERT(initialized_); |
| 166 ASSERT(isolate != NULL); |
| 167 ScopedMonitor lock(monitor_); |
| 168 intptr_t i = FindIsolate(isolate); |
| 169 if (i < 0) { |
| 170 // Not scheduled. |
| 171 return; |
| 172 } |
| 173 { |
| 174 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| 175 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 176 ASSERT(profiler_data != NULL); |
| 177 profiler_data->Descheduled(); |
| 178 } |
| 179 RemoveIsolate(i); |
| 180 lock.Notify(); |
| 181 } |
| 182 |
| 183 |
| 184 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
| 185 ASSERT(isolate == Isolate::Current()); |
| 186 { |
| 187 // We can't get signals here. |
| 188 } |
| 189 UNIMPLEMENTED(); |
| 190 } |
| 191 |
| 192 |
| 193 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { |
| 194 ASSERT(new_capacity < kMaxProfiledIsolates); |
| 195 ASSERT(new_capacity > isolates_capacity_); |
| 196 Isolate* isolate = NULL; |
| 197 isolates_ = reinterpret_cast<Isolate**>( |
| 198 realloc(isolates_, sizeof(isolate) * new_capacity)); |
| 199 isolates_capacity_ = new_capacity; |
| 200 } |
| 201 |
| 202 |
| 203 void ProfilerManager::AddIsolate(Isolate* isolate) { |
| 204 // Must be called with monitor_ locked. |
| 205 if (isolates_size_ == isolates_capacity_) { |
| 206 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); |
| 207 } |
| 208 isolates_[isolates_size_] = isolate; |
| 209 isolates_size_++; |
| 210 } |
| 211 |
| 212 |
| 213 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { |
| 214 // Must be called with monitor_ locked. |
| 215 for (intptr_t i = 0; i < isolates_size_; i++) { |
| 216 if (isolates_[i] == isolate) { |
| 217 return i; |
| 218 } |
| 219 } |
| 220 return -1; |
| 221 } |
| 222 |
| 223 |
| 224 void ProfilerManager::RemoveIsolate(intptr_t i) { |
| 225 // Must be called with monitor_ locked. |
| 226 ASSERT(i < isolates_size_); |
| 227 intptr_t last = isolates_size_ - 1; |
| 228 if (i != last) { |
| 229 isolates_[i] = isolates_[last]; |
| 230 } |
| 231 // Mark last as NULL. |
| 232 isolates_[last] = NULL; |
| 233 // Pop. |
| 234 isolates_size_--; |
| 235 } |
| 236 |
| 237 |
| 238 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { |
| 239 // TODO(johnmccutchan): Differentiate between symbols which can't be found |
| 240 // and symbols which were GCed. (Heap::CodeContains). |
| 241 ASSERT(native_symbol != NULL); |
| 242 const char* symbol_name = "Unknown"; |
| 243 *native_symbol = false; |
| 244 const Code& code = Code::Handle(Code::LookupCode(pc)); |
| 245 if (code.IsNull()) { |
| 246 // Possibly a native symbol. |
| 247 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); |
| 248 if (native_name != NULL) { |
| 249 symbol_name = native_name; |
| 250 *native_symbol = true; |
| 251 } |
| 252 } else { |
| 253 const Function& function = Function::Handle(code.function()); |
| 254 if (!function.IsNull()) { |
| 255 const String& name = String::Handle(function.QualifiedUserVisibleName()); |
| 256 if (!name.IsNull()) { |
| 257 symbol_name = name.ToCString(); |
| 258 } |
| 259 } |
| 260 } |
| 261 return const_cast<char*>(symbol_name); |
| 262 } |
| 263 |
| 264 |
| 265 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, |
| 266 Dart_Port port) { |
| 267 ASSERT(isolate == Isolate::Current()); |
| 268 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); |
| 269 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 270 if (profiler_data == NULL) { |
| 271 return; |
| 272 } |
| 273 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 274 ASSERT(sample_buffer != NULL); |
| 275 JSONStream stream(10 * MB); |
| 276 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); |
| 277 intptr_t pid = 1; |
| 278 { |
| 279 JSONArray events(&stream); |
| 280 { |
| 281 JSONObject thread_name(&events); |
| 282 thread_name.AddProperty("name", "thread_name"); |
| 283 thread_name.AddProperty("ph", "M"); |
| 284 thread_name.AddProperty("tid", tid); |
| 285 thread_name.AddProperty("pid", pid); |
| 286 { |
| 287 JSONObject args(&thread_name, "args"); |
| 288 args.AddProperty("name", name); |
| 289 } |
| 290 } |
| 291 { |
| 292 JSONObject process_name(&events); |
| 293 process_name.AddProperty("name", "process_name"); |
| 294 process_name.AddProperty("ph", "M"); |
| 295 process_name.AddProperty("tid", tid); |
| 296 process_name.AddProperty("pid", pid); |
| 297 { |
| 298 JSONObject args(&process_name, "args"); |
| 299 args.AddProperty("name", "Dart VM"); |
| 300 } |
| 301 } |
| 302 uint64_t last_time = 0; |
| 303 for (Sample* i = sample_buffer->FirstSample(); |
| 304 i != sample_buffer->LastSample(); |
| 305 i = sample_buffer->NextSample(i)) { |
| 306 if (last_time == 0) { |
| 307 last_time = i->timestamp; |
| 308 } |
| 309 intptr_t delta = i->timestamp - last_time; |
| 310 { |
| 311 double percentage = static_cast<double>(i->cpu_usage) / |
| 312 static_cast<double>(delta) * 100.0; |
| 313 if (percentage != percentage) { |
| 314 percentage = 0.0; |
| 315 } |
| 316 percentage = percentage < 0.0 ? 0.0 : percentage; |
| 317 percentage = percentage > 100.0 ? 100.0 : percentage; |
| 318 { |
| 319 JSONObject cpu_usage(&events); |
| 320 cpu_usage.AddProperty("name", "CPU Usage"); |
| 321 cpu_usage.AddProperty("ph", "C"); |
| 322 cpu_usage.AddProperty("tid", tid); |
| 323 cpu_usage.AddProperty("pid", pid); |
| 324 cpu_usage.AddProperty("ts", static_cast<double>(last_time)); |
| 325 { |
| 326 JSONObject args(&cpu_usage, "args"); |
| 327 args.AddProperty("CPU", percentage); |
| 328 } |
| 329 } |
| 330 { |
| 331 JSONObject cpu_usage(&events); |
| 332 cpu_usage.AddProperty("name", "CPU Usage"); |
| 333 cpu_usage.AddProperty("ph", "C"); |
| 334 cpu_usage.AddProperty("tid", tid); |
| 335 cpu_usage.AddProperty("pid", pid); |
| 336 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); |
| 337 { |
| 338 JSONObject args(&cpu_usage, "args"); |
| 339 args.AddProperty("CPU", percentage); |
| 340 } |
| 341 } |
| 342 } |
| 343 for (int j = 0; j < Sample::kNumStackFrames; j++) { |
| 344 if (i->pcs[j] == 0) { |
| 345 continue; |
| 346 } |
| 347 bool native_symbol = false; |
| 348 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| 349 { |
| 350 JSONObject begin(&events); |
| 351 begin.AddProperty("ph", "B"); |
| 352 begin.AddProperty("tid", tid); |
| 353 begin.AddProperty("pid", pid); |
| 354 begin.AddProperty("name", symbol_name); |
| 355 begin.AddProperty("ts", static_cast<double>(last_time)); |
| 356 } |
| 357 if (native_symbol) { |
| 358 NativeSymbolResolver::FreeSymbolName(symbol_name); |
| 359 } |
| 360 } |
| 361 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { |
| 362 if (i->pcs[j] == 0) { |
| 363 continue; |
| 364 } |
| 365 bool native_symbol = false; |
| 366 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| 367 { |
| 368 JSONObject end(&events); |
| 369 end.AddProperty("ph", "E"); |
| 370 end.AddProperty("tid", tid); |
| 371 end.AddProperty("pid", pid); |
| 372 end.AddProperty("name", symbol_name); |
| 373 end.AddProperty("ts", static_cast<double>(i->timestamp)); |
| 374 } |
| 375 if (native_symbol) { |
| 376 NativeSymbolResolver::FreeSymbolName(symbol_name); |
| 377 } |
| 378 } |
| 379 last_time = i->timestamp; |
| 380 } |
| 381 } |
| 382 char fname[1024]; |
| 383 #if defined(TARGET_OS_WINDOWS) |
| 384 snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof", |
| 385 static_cast<int>(port)); |
| 386 #else |
| 387 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", |
| 388 static_cast<int>(port)); |
| 389 #endif |
| 390 printf("%s\n", fname); |
| 391 FILE* f = fopen(fname, "wb"); |
| 392 ASSERT(f != NULL); |
| 393 fputs(stream.ToCString(), f); |
| 394 fclose(f); |
| 395 } |
| 396 |
| 397 |
| 398 |
| 399 |
| 400 |
| 401 IsolateProfilerData::IsolateProfilerData(Isolate* isolate, |
| 402 SampleBuffer* sample_buffer) { |
| 403 isolate_ = isolate; |
| 404 sample_buffer_ = sample_buffer; |
| 405 timer_expiration_micros_ = kNoExpirationTime; |
| 406 last_sampled_micros_ = 0; |
| 407 thread_id_ = 0; |
| 408 } |
| 409 |
| 410 |
| 411 IsolateProfilerData::~IsolateProfilerData() { |
| 412 } |
| 413 |
| 414 |
| 415 void IsolateProfilerData::SampledAt(int64_t current_time) { |
| 416 last_sampled_micros_ = current_time; |
| 417 } |
| 418 |
| 419 |
| 420 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { |
| 421 timer_expiration_micros_ = current_time + sample_interval_micros_; |
| 422 Thread::GetThreadCpuUsage(thread_id, &cpu_usage_); |
| 423 thread_id_ = thread_id; |
| 424 } |
| 425 |
| 426 |
| 427 void IsolateProfilerData::Descheduled() { |
| 428 // TODO(johnmccutchan): Track when we ran for a fraction of our sample |
| 429 // interval and incorporate the time difference when scheduling the |
| 430 // isolate again. |
| 431 cpu_usage_ = kDescheduledCpuUsage; |
| 432 timer_expiration_micros_ = kNoExpirationTime; |
| 433 thread_id_ = 0; |
| 434 Sample* sample = sample_buffer_->ReserveSample(); |
| 435 ASSERT(sample != NULL); |
| 436 sample->timestamp = OS::GetCurrentTimeMicros(); |
| 437 sample->cpu_usage = 0; |
| 438 sample->vm_tags = Sample::kIdle; |
| 439 } |
| 440 |
| 441 |
| 442 const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; |
| 443 const char* Sample::kNoSymbol = "No Symbol Found"; |
| 444 |
| 445 Sample::Sample() { |
| 446 timestamp = 0; |
| 447 cpu_usage = 0; |
| 448 for (int i = 0; i < kNumStackFrames; i++) { |
| 449 pcs[i] = 0; |
| 450 } |
| 451 vm_tags = kIdle; |
| 452 runtime_tags = 0; |
| 453 } |
| 454 |
| 455 |
| 456 SampleBuffer::SampleBuffer(intptr_t capacity) { |
| 457 start_ = 0; |
| 458 end_ = 0; |
| 459 capacity_ = capacity; |
| 460 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); |
| 461 } |
| 462 |
| 463 |
| 464 SampleBuffer::~SampleBuffer() { |
| 465 if (samples_ != NULL) { |
| 466 free(samples_); |
| 467 samples_ = NULL; |
| 468 } |
| 469 } |
| 470 |
| 471 |
| 472 Sample* SampleBuffer::ReserveSample() { |
| 473 intptr_t index = end_; |
| 474 end_ = WrapIncrement(end_); |
| 475 if (end_ == start_) { |
| 476 start_ = WrapIncrement(start_); |
| 477 } |
| 478 // Reset. |
| 479 samples_[index] = Sample(); |
| 480 return &samples_[index]; |
| 481 } |
| 482 |
| 483 |
| 484 Sample* SampleBuffer::FirstSample() const { |
| 485 return &samples_[start_]; |
| 486 } |
| 487 |
| 488 |
| 489 Sample* SampleBuffer::NextSample(Sample* sample) const { |
| 490 ASSERT(sample >= &samples_[0]); |
| 491 ASSERT(sample < &samples_[capacity_]); |
| 492 intptr_t index = sample - samples_; |
| 493 index = WrapIncrement(index); |
| 494 return &samples_[index]; |
| 495 } |
| 496 |
| 497 |
| 498 Sample* SampleBuffer::LastSample() const { |
| 499 return &samples_[end_]; |
| 500 } |
| 501 |
| 502 |
| 503 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { |
| 504 return (i + 1) % capacity_; |
| 505 } |
| 506 |
| 507 |
| 508 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, |
| 509 uintptr_t stack_lower, |
| 510 uintptr_t stack_upper, |
| 511 uintptr_t pc, |
| 512 uintptr_t fp, |
| 513 uintptr_t sp) : |
| 514 sample_(sample), |
| 515 stack_lower_(stack_lower), |
| 516 stack_upper_(stack_upper), |
| 517 original_pc_(pc), |
| 518 original_fp_(fp), |
| 519 original_sp_(sp) { |
| 520 ASSERT(sample_ != NULL); |
| 521 } |
| 522 |
| 523 |
| 524 int ProfilerSampleStackWalker::walk() { |
| 525 uword* pc = reinterpret_cast<uword*>(original_pc_); |
| 526 uword* fp = reinterpret_cast<uword*>(original_fp_); |
| 527 int i = 0; |
| 528 for (; i < Sample::kNumStackFrames; i++) { |
| 529 sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc); |
| 530 if (!ValidFramePointer(fp)) { |
| 531 break; |
| 532 } |
| 533 pc = CallerPC(fp); |
| 534 uword* previous_fp = fp; |
| 535 fp = CallerFP(fp); |
| 536 if (fp <= previous_fp) { |
| 537 // Frame pointers should only move to higher addresses. |
| 538 break; |
| 539 } |
| 540 } |
| 541 return i; |
| 542 } |
| 543 |
| 544 |
| 545 uword* ProfilerSampleStackWalker::CallerPC(uword* fp) { |
| 546 ASSERT(fp != NULL); |
| 547 return reinterpret_cast<uword*>(*(fp+1)); |
| 548 } |
| 549 |
| 550 |
| 551 uword* ProfilerSampleStackWalker::CallerFP(uword* fp) { |
| 552 ASSERT(fp != NULL); |
| 553 return reinterpret_cast<uword*>(*fp); |
| 554 } |
| 555 |
| 556 |
| 557 bool ProfilerSampleStackWalker::ValidFramePointer(uword* fp) { |
| 558 if (fp == NULL) { |
| 559 return false; |
| 560 } |
| 561 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); |
| 562 cursor += sizeof(fp); |
| 563 bool r = cursor >= stack_lower_ && cursor <= stack_upper_; |
| 564 return r; |
| 565 } |
| 566 |
| 567 |
| 568 } // namespace dart |
OLD | NEW |