Chromium Code Reviews| 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 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); | |
| 19 | |
| 20 bool ProfilerManager::initialized_ = false; | |
| 21 bool ProfilerManager::shutdown_ = false; | |
| 22 Monitor* ProfilerManager::monitor_ = NULL; | |
| 23 Isolate** ProfilerManager::isolates_ = NULL; | |
| 24 intptr_t ProfilerManager::isolates_capacity_ = 0; | |
| 25 intptr_t ProfilerManager::isolates_size_ = 0; | |
| 26 | |
| 27 | |
| 28 void ProfilerManager::InitOnce() { | |
| 29 if (!FLAG_profile) { | |
| 30 return; | |
| 31 } | |
| 32 NativeSymbolResolver::InitOnce(); | |
| 33 ASSERT(!initialized_); | |
| 34 monitor_ = new Monitor(); | |
| 35 initialized_ = true; | |
| 36 ResizeIsolates(16); | |
| 37 Thread::Start(ThreadMain, 0); | |
| 38 } | |
| 39 | |
| 40 | |
| 41 void ProfilerManager::Shutdown() { | |
| 42 if (!FLAG_profile) { | |
| 43 return; | |
| 44 } | |
| 45 ScopedMonitorLock lock(monitor_); | |
| 46 shutdown_ = true; | |
| 47 for (intptr_t i = 0; i < isolates_size_; i++) { | |
| 48 Isolate* isolate = isolates_[i]; | |
| 49 ASSERT(isolate != NULL); | |
| 50 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
| 51 FreeIsolateProfilingData(isolate); | |
| 52 } | |
| 53 isolates_size_ = 0; | |
|
siva
2013/11/11 03:51:54
a free of isolates_ also needs to be done here?
Cutch
2013/11/18 20:48:54
Done.
| |
| 54 lock.Notify(); | |
| 55 NativeSymbolResolver::ShutdownOnce(); | |
| 56 } | |
| 57 | |
| 58 | |
| 59 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { | |
| 60 if (!FLAG_profile) { | |
| 61 return; | |
| 62 } | |
| 63 ASSERT(isolate != NULL); | |
| 64 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
| 65 SampleBuffer* sample_buffer = new SampleBuffer(); | |
| 66 IsolateProfilerData* profiler_data = | |
| 67 new IsolateProfilerData(isolate, sample_buffer); | |
| 68 profiler_data->set_sample_interval(1000); | |
| 69 isolate->set_profiler_data(profiler_data); | |
| 70 } | |
| 71 | |
| 72 | |
| 73 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { | |
| 74 // TODO(johnmccutchan): Move ScopedMutexLock here and remove from | |
| 75 // two call sites. | |
| 76 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 77 if (profiler_data == NULL) { | |
| 78 // Already freed. | |
| 79 return; | |
| 80 } | |
| 81 isolate->set_profiler_data(NULL); | |
| 82 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
| 83 ASSERT(sample_buffer != NULL); | |
| 84 delete sample_buffer; | |
| 85 delete profiler_data; | |
| 86 } | |
| 87 | |
| 88 | |
| 89 void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) { | |
| 90 ASSERT(isolate != NULL); | |
| 91 if (!FLAG_profile) { | |
| 92 return; | |
| 93 } | |
| 94 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
| 95 FreeIsolateProfilingData(isolate); | |
| 96 } | |
| 97 | |
| 98 | |
| 99 void ProfilerManager::ScheduleIsolate(Isolate* isolate) { | |
| 100 if (!FLAG_profile) { | |
| 101 return; | |
| 102 } | |
| 103 ASSERT(initialized_); | |
| 104 ASSERT(isolate != NULL); | |
| 105 ScopedMonitorLock lock(monitor_); | |
| 106 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
|
siva
2013/11/11 03:51:54
Maybe later you should consider adding a locking o
| |
| 107 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 108 if (profiler_data == NULL) { | |
| 109 return; | |
| 110 } | |
| 111 profiler_data->Scheduled(OS::GetCurrentTimeMicros(), | |
| 112 Thread::GetCurrentThreadId()); | |
| 113 AddIsolate(isolate); | |
| 114 lock.Notify(); | |
| 115 } | |
| 116 | |
| 117 | |
| 118 void ProfilerManager::DescheduleIsolate(Isolate* isolate) { | |
| 119 if (!FLAG_profile) { | |
| 120 return; | |
| 121 } | |
| 122 ASSERT(initialized_); | |
| 123 ASSERT(isolate != NULL); | |
| 124 ScopedMonitorLock lock(monitor_); | |
| 125 intptr_t i = FindIsolate(isolate); | |
| 126 if (i < 0) { | |
| 127 // Not scheduled. | |
| 128 return; | |
| 129 } | |
| 130 { | |
| 131 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
| 132 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 133 ASSERT(profiler_data != NULL); | |
| 134 profiler_data->Descheduled(); | |
| 135 } | |
| 136 RemoveIsolate(i); | |
| 137 lock.Notify(); | |
| 138 } | |
| 139 | |
| 140 | |
| 141 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { | |
| 142 ASSERT(isolate == Isolate::Current()); | |
| 143 { | |
| 144 // We can't get signals here. | |
| 145 } | |
| 146 UNIMPLEMENTED(); | |
| 147 } | |
| 148 | |
| 149 | |
| 150 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { | |
| 151 ASSERT(new_capacity > isolates_capacity_); | |
| 152 Isolate* isolate = NULL; | |
| 153 isolates_ = reinterpret_cast<Isolate**>( | |
| 154 realloc(isolates_, sizeof(isolate) * new_capacity)); | |
|
siva
2013/11/11 03:51:54
we need to ensure that this does not overflow, may
Cutch
2013/11/18 20:48:54
Done. For now I just assert we aren't monitoring m
| |
| 155 isolates_capacity_ = new_capacity; | |
| 156 } | |
| 157 | |
| 158 | |
| 159 void ProfilerManager::AddIsolate(Isolate* isolate) { | |
|
siva
2013/11/11 03:51:54
you should add a comment here and in FindIsolate,
Cutch
2013/11/18 20:48:54
Done.
| |
| 160 if (isolates_size_ == isolates_capacity_) { | |
| 161 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); | |
| 162 } | |
| 163 isolates_[isolates_size_] = isolate; | |
| 164 isolates_size_++; | |
| 165 } | |
| 166 | |
| 167 | |
| 168 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { | |
| 169 for (intptr_t i = 0; i < isolates_size_; i++) { | |
| 170 if (isolates_[i] == isolate) { | |
| 171 return i; | |
| 172 } | |
| 173 } | |
| 174 return -1; | |
| 175 } | |
| 176 | |
| 177 | |
| 178 void ProfilerManager::RemoveIsolate(intptr_t i) { | |
| 179 ASSERT(i < isolates_size_); | |
| 180 intptr_t last = isolates_size_ - 1; | |
| 181 if (i != last) { | |
| 182 isolates_[i] = isolates_[last]; | |
| 183 } | |
| 184 // Mark last as NULL. | |
| 185 isolates_[last] = NULL; | |
| 186 // Pop. | |
| 187 isolates_size_--; | |
| 188 } | |
| 189 | |
| 190 | |
| 191 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { | |
| 192 // TODO(johnmccutchan): Differentiate between symbols which can't be found | |
| 193 // and symbols which were GCed. (Heap::CodeContains). | |
| 194 ASSERT(native_symbol != NULL); | |
| 195 const char* symbol_name = "Unknown"; | |
| 196 *native_symbol = false; | |
| 197 const Code& code = Code::Handle(Code::LookupCode(pc)); | |
| 198 if (code.IsNull()) { | |
| 199 // Possibly a native symbol. | |
| 200 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); | |
| 201 if (native_name != NULL) { | |
| 202 symbol_name = native_name; | |
| 203 *native_symbol = true; | |
| 204 } | |
| 205 } else { | |
| 206 const Function& function = Function::Handle(code.function()); | |
| 207 if (!function.IsNull()) { | |
| 208 const String& name = String::Handle(function.QualifiedUserVisibleName()); | |
| 209 if (!name.IsNull()) { | |
| 210 symbol_name = name.ToCString(); | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 return const_cast<char*>(symbol_name); | |
| 215 } | |
| 216 | |
| 217 | |
| 218 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, | |
| 219 Dart_Port port) { | |
| 220 ASSERT(isolate == Isolate::Current()); | |
| 221 ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); | |
| 222 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
| 223 if (profiler_data == NULL) { | |
| 224 return; | |
| 225 } | |
| 226 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
| 227 ASSERT(sample_buffer != NULL); | |
| 228 JSONStream stream(10 * MB); | |
| 229 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); | |
| 230 intptr_t pid = 1; | |
| 231 { | |
| 232 JSONArray events(&stream); | |
| 233 { | |
| 234 JSONObject thread_name(&events); | |
| 235 thread_name.AddProperty("name", "thread_name"); | |
| 236 thread_name.AddProperty("ph", "M"); | |
| 237 thread_name.AddProperty("tid", tid); | |
| 238 thread_name.AddProperty("pid", pid); | |
| 239 { | |
| 240 JSONObject args(&thread_name, "args"); | |
| 241 args.AddProperty("name", name); | |
| 242 } | |
| 243 } | |
| 244 { | |
| 245 JSONObject process_name(&events); | |
| 246 process_name.AddProperty("name", "process_name"); | |
| 247 process_name.AddProperty("ph", "M"); | |
| 248 process_name.AddProperty("tid", tid); | |
| 249 process_name.AddProperty("pid", pid); | |
| 250 { | |
| 251 JSONObject args(&process_name, "args"); | |
| 252 args.AddProperty("name", "Dart VM"); | |
| 253 } | |
| 254 } | |
| 255 uint64_t last_time = 0; | |
| 256 for (Sample* i = sample_buffer->FirstSample(); | |
| 257 i != sample_buffer->LastSample(); | |
| 258 i = sample_buffer->NextSample(i)) { | |
| 259 if (last_time == 0) { | |
| 260 last_time = i->timestamp; | |
| 261 } | |
| 262 intptr_t delta = i->timestamp - last_time; | |
| 263 { | |
| 264 double percentage = static_cast<double>(i->cpu_usage) / | |
| 265 static_cast<double>(delta) * 100.0; | |
| 266 if (percentage != percentage) { | |
| 267 percentage = 0.0; | |
| 268 } | |
| 269 percentage = percentage < 0.0 ? 0.0 : percentage; | |
| 270 percentage = percentage > 100.0 ? 100.0 : percentage; | |
| 271 { | |
| 272 JSONObject cpu_usage(&events); | |
| 273 cpu_usage.AddProperty("name", "CPU Usage"); | |
| 274 cpu_usage.AddProperty("ph", "C"); | |
| 275 cpu_usage.AddProperty("tid", tid); | |
| 276 cpu_usage.AddProperty("pid", pid); | |
| 277 cpu_usage.AddProperty("ts", static_cast<double>(last_time)); | |
| 278 { | |
| 279 JSONObject args(&cpu_usage, "args"); | |
| 280 args.AddProperty("CPU", percentage); | |
| 281 } | |
| 282 } | |
| 283 { | |
| 284 JSONObject cpu_usage(&events); | |
| 285 cpu_usage.AddProperty("name", "CPU Usage"); | |
| 286 cpu_usage.AddProperty("ph", "C"); | |
| 287 cpu_usage.AddProperty("tid", tid); | |
| 288 cpu_usage.AddProperty("pid", pid); | |
| 289 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); | |
| 290 { | |
| 291 JSONObject args(&cpu_usage, "args"); | |
| 292 args.AddProperty("CPU", percentage); | |
| 293 } | |
| 294 } | |
| 295 } | |
| 296 for (int j = 0; j < Sample::kNumStackFrames; j++) { | |
| 297 if (i->pcs[j] == 0) { | |
| 298 continue; | |
| 299 } | |
| 300 bool native_symbol = false; | |
| 301 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
| 302 { | |
| 303 JSONObject begin(&events); | |
| 304 begin.AddProperty("ph", "B"); | |
| 305 begin.AddProperty("tid", tid); | |
| 306 begin.AddProperty("pid", pid); | |
| 307 begin.AddProperty("name", symbol_name); | |
| 308 begin.AddProperty("ts", static_cast<double>(last_time)); | |
| 309 } | |
| 310 if (native_symbol) { | |
| 311 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
| 312 } | |
| 313 } | |
| 314 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { | |
| 315 if (i->pcs[j] == 0) { | |
| 316 continue; | |
| 317 } | |
| 318 bool native_symbol = false; | |
| 319 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
| 320 { | |
| 321 JSONObject end(&events); | |
| 322 end.AddProperty("ph", "E"); | |
| 323 end.AddProperty("tid", tid); | |
| 324 end.AddProperty("pid", pid); | |
| 325 end.AddProperty("name", symbol_name); | |
| 326 end.AddProperty("ts", static_cast<double>(i->timestamp)); | |
| 327 } | |
| 328 if (native_symbol) { | |
| 329 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
| 330 } | |
| 331 } | |
| 332 last_time = i->timestamp; | |
| 333 } | |
| 334 } | |
| 335 char fname[1024]; | |
| 336 #if defined(TARGET_OS_WINDOWS) | |
| 337 snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof", | |
| 338 static_cast<int>(port)); | |
| 339 #else | |
| 340 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", | |
| 341 static_cast<int>(port)); | |
| 342 #endif | |
| 343 printf("%s\n", fname); | |
| 344 FILE* f = fopen(fname, "wb"); | |
| 345 ASSERT(f != NULL); | |
| 346 fputs(stream.ToCString(), f); | |
| 347 fclose(f); | |
| 348 } | |
| 349 | |
| 350 | |
| 351 | |
| 352 | |
| 353 | |
| 354 IsolateProfilerData::IsolateProfilerData(Isolate* isolate, | |
| 355 SampleBuffer* sample_buffer) { | |
| 356 isolate_ = isolate; | |
| 357 sample_buffer_ = sample_buffer; | |
| 358 timer_expiration_micros_ = kNoExpirationTime; | |
| 359 last_sampled_micros_ = 0; | |
| 360 thread_id_ = 0; | |
| 361 } | |
| 362 | |
| 363 | |
| 364 IsolateProfilerData::~IsolateProfilerData() { | |
| 365 } | |
| 366 | |
| 367 | |
| 368 void IsolateProfilerData::SampledAt(int64_t current_time) { | |
| 369 last_sampled_micros_ = current_time; | |
| 370 } | |
| 371 | |
| 372 | |
| 373 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { | |
| 374 timer_expiration_micros_ = current_time + sample_interval_micros_; | |
| 375 Thread::GetThreadCpuUsage(thread_id, &cpu_usage_); | |
| 376 thread_id_ = thread_id; | |
| 377 } | |
| 378 | |
| 379 | |
| 380 void IsolateProfilerData::Descheduled() { | |
| 381 cpu_usage_ = kDescheduledCpuUsage; | |
| 382 timer_expiration_micros_ = kNoExpirationTime; | |
| 383 thread_id_ = 0; | |
| 384 Sample* sample = sample_buffer_->ReserveSample(); | |
| 385 ASSERT(sample != NULL); | |
| 386 sample->timestamp = OS::GetCurrentTimeMicros(); | |
| 387 sample->cpu_usage = 0; | |
| 388 sample->vm_tags = Sample::kIdle; | |
| 389 } | |
| 390 | |
| 391 | |
| 392 const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; | |
| 393 const char* Sample::kNoSymbol = "No Symbol Found"; | |
| 394 | |
| 395 Sample::Sample() { | |
| 396 timestamp = 0; | |
| 397 cpu_usage = 0; | |
| 398 for (int i = 0; i < kNumStackFrames; i++) { | |
| 399 pcs[i] = 0; | |
| 400 } | |
| 401 vm_tags = kIdle; | |
| 402 runtime_tags = 0; | |
| 403 } | |
| 404 | |
| 405 | |
| 406 SampleBuffer::SampleBuffer(intptr_t capacity) { | |
| 407 start_ = 0; | |
| 408 end_ = 0; | |
| 409 capacity_ = capacity; | |
| 410 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); | |
| 411 } | |
| 412 | |
| 413 | |
| 414 SampleBuffer::~SampleBuffer() { | |
| 415 if (samples_ != NULL) { | |
| 416 free(samples_); | |
| 417 samples_ = NULL; | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 | |
| 422 Sample* SampleBuffer::ReserveSample() { | |
| 423 intptr_t index = end_; | |
| 424 end_ = WrapIncrement(end_); | |
| 425 if (end_ == start_) { | |
| 426 start_ = WrapIncrement(start_); | |
| 427 } | |
| 428 // Reset. | |
| 429 samples_[index] = Sample(); | |
| 430 return &samples_[index]; | |
| 431 } | |
| 432 | |
| 433 | |
| 434 Sample* SampleBuffer::FirstSample() const { | |
| 435 return &samples_[start_]; | |
| 436 } | |
| 437 | |
| 438 | |
| 439 Sample* SampleBuffer::NextSample(Sample* sample) const { | |
| 440 ASSERT(sample >= &samples_[0]); | |
| 441 ASSERT(sample < &samples_[capacity_]); | |
| 442 intptr_t index = sample - samples_; | |
| 443 index = WrapIncrement(index); | |
| 444 return &samples_[index]; | |
| 445 } | |
| 446 | |
| 447 | |
| 448 Sample* SampleBuffer::LastSample() const { | |
| 449 return &samples_[end_]; | |
| 450 } | |
| 451 | |
| 452 | |
| 453 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { | |
| 454 return (i + 1) % capacity_; | |
| 455 } | |
| 456 | |
| 457 | |
| 458 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, | |
| 459 uintptr_t stack_lower, | |
| 460 uintptr_t stack_upper, | |
| 461 uintptr_t pc, | |
| 462 uintptr_t fp, | |
| 463 uintptr_t sp) : | |
| 464 sample_(sample), | |
| 465 stack_lower_(stack_lower), | |
| 466 stack_upper_(stack_upper), | |
| 467 original_pc_(pc), | |
| 468 original_fp_(fp), | |
| 469 original_sp_(sp) { | |
| 470 ASSERT(sample_ != NULL); | |
| 471 } | |
| 472 | |
| 473 | |
| 474 int ProfilerSampleStackWalker::walk() { | |
| 475 uword* pc = reinterpret_cast<uword*>(original_pc_); | |
| 476 uword* fp = reinterpret_cast<uword*>(original_fp_); | |
| 477 int i = 0; | |
| 478 for (; i < Sample::kNumStackFrames; i++) { | |
| 479 sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc); | |
| 480 if (!ValidInstructionPointer(pc) || !ValidFramePointer(fp)) { | |
| 481 break; | |
| 482 } | |
| 483 pc = CallerPC(fp); | |
| 484 uword* previous_fp = fp; | |
| 485 fp = CallerFP(fp); | |
| 486 if (fp <= previous_fp) { | |
| 487 // Frame pointers should only move to higher addresses. | |
| 488 break; | |
| 489 } | |
| 490 } | |
| 491 return i; | |
| 492 } | |
| 493 | |
| 494 | |
| 495 uword* ProfilerSampleStackWalker::CallerPC(uword* fp) { | |
| 496 ASSERT(fp != NULL); | |
| 497 return reinterpret_cast<uword*>(*(fp+1)); | |
| 498 } | |
| 499 | |
| 500 | |
| 501 uword* ProfilerSampleStackWalker::CallerFP(uword* fp) { | |
| 502 ASSERT(fp != NULL); | |
| 503 return reinterpret_cast<uword*>(*fp); | |
| 504 } | |
| 505 | |
| 506 | |
| 507 bool ProfilerSampleStackWalker::ValidInstructionPointer(uword* pc) { | |
| 508 uintptr_t cursor = reinterpret_cast<uintptr_t>(pc); | |
| 509 return cursor != 0; | |
|
siva
2013/11/11 03:51:54
Seems simplistic...
Cutch
2013/11/18 20:48:54
Done.
| |
| 510 } | |
| 511 | |
| 512 | |
| 513 bool ProfilerSampleStackWalker::ValidFramePointer(uword* fp) { | |
| 514 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); | |
| 515 cursor += sizeof(fp); | |
| 516 bool r = cursor >= stack_lower_ && cursor <= stack_upper_; | |
| 517 return r; | |
| 518 } | |
| 519 | |
| 520 | |
| 521 } // namespace dart | |
| OLD | NEW |