OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 <cstdio> | 5 #include <cstdio> |
6 | 6 |
7 #include "platform/utils.h" | 7 #include "platform/utils.h" |
8 | 8 |
9 #include "vm/allocation.h" | |
9 #include "vm/atomic.h" | 10 #include "vm/atomic.h" |
11 #include "vm/code_patcher.h" | |
10 #include "vm/isolate.h" | 12 #include "vm/isolate.h" |
11 #include "vm/json_stream.h" | 13 #include "vm/json_stream.h" |
12 #include "vm/native_symbol.h" | 14 #include "vm/native_symbol.h" |
13 #include "vm/object.h" | 15 #include "vm/object.h" |
14 #include "vm/os.h" | 16 #include "vm/os.h" |
15 #include "vm/profiler.h" | 17 #include "vm/profiler.h" |
16 #include "vm/signal_handler.h" | 18 #include "vm/signal_handler.h" |
17 #include "vm/simulator.h" | 19 #include "vm/simulator.h" |
18 | 20 |
19 namespace dart { | 21 namespace dart { |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
211 if ((stack_lower == 0) || (stack_upper == 0)) { | 213 if ((stack_lower == 0) || (stack_upper == 0)) { |
212 stack_lower = 0; | 214 stack_lower = 0; |
213 stack_upper = 0; | 215 stack_upper = 0; |
214 } | 216 } |
215 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, | 217 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, |
216 state.pc, state.fp, state.sp); | 218 state.pc, state.fp, state.sp); |
217 stackWalker.walk(); | 219 stackWalker.walk(); |
218 } | 220 } |
219 | 221 |
220 | 222 |
223 class SymbolLookupCache { | |
224 public: | |
225 explicit SymbolLookupCache(const intptr_t cache_size = 32) { | |
226 cache_index_ = 0; | |
227 cache_size_ = cache_size; | |
228 pcs_ = reinterpret_cast<uintptr_t*>( | |
229 calloc(cache_size, sizeof(uintptr_t))); // NOLINT | |
230 names_ = reinterpret_cast<const char**>( | |
231 calloc(cache_size, sizeof(const char*))); // NOLINT | |
232 } | |
233 | |
234 virtual ~SymbolLookupCache() { | |
235 for (intptr_t i = 0; i < cache_size_; i++) { | |
236 ClearCacheIndex(i); | |
237 } | |
238 free(pcs_); | |
239 free(names_); | |
240 } | |
241 | |
242 const char* Lookup(uintptr_t pc) { | |
243 const char* symbol = LookupInCache(pc); | |
244 if (symbol != NULL) { | |
245 return symbol; | |
246 } | |
247 symbol = QuerySource(pc); | |
248 if (symbol != NULL) { | |
249 InsertIntoCache(pc, symbol); | |
250 } | |
251 return symbol; | |
252 } | |
253 | |
254 virtual const char* QuerySource(uintptr_t pc) const = 0; | |
255 | |
256 protected: | |
257 const char* GenerateSymbolName(const char* prefix, uintptr_t pc) const { | |
258 // Generate a name by binning into buckets by PC. | |
259 const intptr_t kBucketSize = 256; | |
260 const intptr_t kBucketMask = ~(kBucketSize - 1); | |
261 pc &= kBucketMask; | |
262 const intptr_t kBuffSize = 256; | |
263 char buff[kBuffSize]; | |
264 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | |
265 prefix, pc, pc + kBucketSize); | |
266 return strdup(buff); | |
267 } | |
268 | |
269 private: | |
270 const char* LookupInCache(uintptr_t pc) { | |
271 for (intptr_t i = 0; i < cache_size_; i++) { | |
272 if (pcs_[i] == pc) { | |
273 ASSERT(names_[i] != NULL); | |
274 return names_[i]; | |
275 } | |
276 } | |
277 return NULL; | |
278 } | |
279 | |
280 intptr_t GetCacheIndex(uintptr_t pc) { | |
281 cache_index_ = (cache_index_ + 1) % cache_size_; | |
282 return cache_index_; | |
283 } | |
284 | |
285 void InsertIntoCache(uintptr_t pc, const char* name) { | |
286 intptr_t index = GetCacheIndex(pc); | |
287 ClearCacheIndex(index); | |
288 pcs_[index] = pc; | |
289 names_[index] = name; | |
290 } | |
291 | |
292 void ClearCacheIndex(intptr_t index) { | |
293 free(const_cast<char*>(names_[index])); | |
294 names_[index] = NULL; | |
295 pcs_[index] = 0; | |
296 } | |
297 | |
298 intptr_t cache_index_; | |
299 intptr_t cache_size_; | |
300 uintptr_t* pcs_; | |
301 const char** names_; | |
302 }; | |
303 | |
304 | |
305 class NativeSymbolLookup : public SymbolLookupCache { | |
306 public: | |
307 NativeSymbolLookup() : SymbolLookupCache(32) { | |
308 } | |
309 | |
310 const char* QuerySource(uintptr_t pc) const { | |
311 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); | |
312 if (native_name == NULL) { | |
313 return GenerateSymbolName("Unknown", pc); | |
314 } | |
315 return native_name; | |
316 } | |
317 }; | |
318 | |
319 | |
320 class DartSymbolLookup : public SymbolLookupCache { | |
321 public: | |
322 explicit DartSymbolLookup(Isolate* isolate) : SymbolLookupCache(32), | |
323 isolate_(isolate), | |
324 heap_(isolate_->heap()) { | |
325 } | |
326 | |
327 bool InDartCodeSpace(uintptr_t pc) { | |
328 ASSERT(heap_ != NULL); | |
329 return heap_->CodeContains(pc); | |
330 } | |
331 | |
332 const char* QuerySource(uintptr_t pc) const { | |
333 const Code& code = Code::Handle(isolate_, Code::LookupCode(pc)); | |
334 if (!code.IsNull()) { | |
335 const Function& function = Function::Handle(isolate_, code.function()); | |
336 if (!function.IsNull()) { | |
337 const String& name = | |
338 String::Handle(isolate_, function.QualifiedUserVisibleName()); | |
339 if (!name.IsNull()) { | |
340 return strdup(name.ToCString()); | |
341 } | |
342 } else { | |
343 return GenerateSymbolName("Detached", pc); | |
344 } | |
345 } | |
346 return GenerateSymbolName("Collected", pc); | |
347 } | |
348 | |
349 private: | |
350 Isolate* isolate_; | |
351 Heap* heap_; | |
352 }; | |
353 | |
354 | |
355 class DartCodeTable { | |
356 public: | |
357 explicit DartCodeTable(Isolate* isolate) : | |
358 heap_(isolate->heap()), | |
359 code_table_(GrowableObjectArray::Handle(isolate)) { | |
360 code_table_ ^= GrowableObjectArray::New(); | |
361 ASSERT(!code_table_.IsNull()); | |
362 } | |
363 | |
364 ~DartCodeTable() { | |
365 } | |
366 | |
367 bool Contains(const Code& code) { | |
368 intptr_t length = code_table_.Length(); | |
369 Code& table_code = Code::Handle(); | |
370 for (intptr_t i = 0; i < length; i++) { | |
371 table_code ^= code_table_.At(i); | |
372 if (table_code.IsNull()) { | |
373 return false; | |
374 } | |
375 ASSERT(!table_code.IsNull()); | |
376 if (table_code.raw() == code.raw()) { | |
377 return true; | |
378 } | |
379 } | |
380 return false; | |
381 } | |
382 | |
383 void Add(const Code& code) { | |
384 ASSERT(!Contains(code)); | |
385 code_table_.Add(code); | |
386 } | |
387 | |
388 private: | |
389 Heap* heap_; | |
390 GrowableObjectArray& code_table_; | |
391 }; | |
392 | |
393 | |
394 class ProfilerSymbolHelper { | |
395 public: | |
396 explicit ProfilerSymbolHelper(Isolate* isolate) : | |
397 dart_symbol_(isolate), | |
398 code_table_(isolate) { | |
399 } | |
400 | |
401 ~ProfilerSymbolHelper() { | |
402 } | |
403 | |
404 | |
405 const char* GetSymbol(uintptr_t pc) { | |
406 if (pc == 0) { | |
407 return "<no frame>"; | |
408 } | |
409 if (dart_symbol_.InDartCodeSpace(pc)) { | |
Ivan Posva
2013/12/30 23:06:09
Restructuring this as discussed will avoid iterati
| |
410 return dart_symbol_.Lookup(pc); | |
411 } else { | |
412 return native_symbol_.Lookup(pc); | |
413 } | |
414 } | |
415 | |
416 | |
417 RawCode* GetNewCode(uintptr_t pc) { | |
418 Code& new_code = Code::Handle(); | |
419 new_code ^= Code::LookupCode(pc); | |
Ivan Posva
2013/12/30 23:06:09
Here you are walking through the heap again. Looki
| |
420 if (!new_code.IsNull()) { | |
421 Function& function = Function::Handle(new_code.function()); | |
422 if (function.IsNull()) { | |
423 // If code no longer has a function, ignore it. | |
424 new_code ^= Code::null(); | |
425 } else if (code_table_.Contains(new_code)) { | |
426 // If code has been serialized, ignore it. | |
427 new_code ^= Code::null(); | |
428 } else { | |
429 // Add code to code table. | |
430 code_table_.Add(new_code); | |
431 } | |
432 } | |
433 return new_code.raw(); | |
434 } | |
435 | |
436 private: | |
437 DartSymbolLookup dart_symbol_; | |
438 DartCodeTable code_table_; | |
439 NativeSymbolLookup native_symbol_; | |
440 }; | |
441 | |
442 | |
221 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { | 443 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
222 ASSERT(isolate == Isolate::Current()); | 444 ASSERT(isolate == Isolate::Current()); |
223 UNIMPLEMENTED(); | 445 // Disable profile interrupts while processing the buffer. |
446 EndExecution(isolate); | |
447 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | |
448 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
449 if (profiler_data == NULL) { | |
450 return; | |
451 } | |
452 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
453 ASSERT(sample_buffer != NULL); | |
454 ProfilerSymbolHelper symbol_helper(isolate); | |
455 { | |
456 JSONArray events(stream); | |
457 for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { | |
458 Sample sample = sample_buffer->GetSample(i); | |
459 if (sample.isolate != isolate) { | |
460 continue; | |
461 } | |
462 if (sample.timestamp == 0) { | |
463 continue; | |
464 } | |
465 WriteSample(isolate, &symbol_helper, &sample, events); | |
466 } | |
467 } | |
468 // Enable profile interrupts. | |
469 BeginExecution(isolate); | |
224 } | 470 } |
225 | 471 |
226 | 472 |
227 static const char* FindSymbolName(uintptr_t pc, bool* symbol_name_allocated) { | 473 void Profiler::WriteSample(Isolate* isolate, |
228 // TODO(johnmccutchan): Differentiate between symbols which can't be found | 474 ProfilerSymbolHelper* symbol_helper, |
229 // and symbols which were GCed. (Heap::CodeContains). | 475 Sample* sample, JSONArray& events) { |
230 ASSERT(symbol_name_allocated != NULL); | 476 StackZone zone(isolate); |
231 const char* symbol_name = "Unknown"; | 477 Code& new_code = Code::Handle(isolate); |
232 *symbol_name_allocated = false; | 478 Sample::SampleType type = sample->type; |
233 if (pc == 0) { | 479 if (type != Sample::kIsolateSample) { |
234 return const_cast<char*>(Sample::kNoFrame); | 480 return; |
235 } | 481 } |
236 const Code& code = Code::Handle(Code::LookupCode(pc)); | 482 double timestamp = static_cast<double>(sample->timestamp); |
237 if (!code.IsNull()) { | 483 bool any = false; |
238 const Function& function = Function::Handle(code.function()); | 484 for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
239 if (!function.IsNull()) { | 485 if (sample->pcs[i] == 0) { |
240 const String& name = String::Handle(function.QualifiedUserVisibleName()); | 486 continue; |
241 if (!name.IsNull()) { | 487 } |
242 symbol_name = name.ToCString(); | 488 any = true; |
243 return symbol_name; | 489 } |
244 } | 490 if (!any) { |
245 } | 491 // No frames in this sample. |
246 } else { | 492 return; |
247 // Possibly a native symbol. | 493 } |
248 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); | |
249 if (native_name != NULL) { | |
250 symbol_name = native_name; | |
251 *symbol_name_allocated = true; | |
252 return symbol_name; | |
253 } | |
254 } | |
255 const intptr_t kBucketSize = 256; | |
256 const intptr_t kBucketMask = ~(kBucketSize - 1); | |
257 // Not a Dart symbol or a native symbol. Bin into buckets by PC. | |
258 pc &= kBucketMask; | |
259 { | 494 { |
260 const intptr_t kBuffSize = 256; | 495 JSONObject sample_event(&events); |
261 char buff[kBuffSize]; | 496 sample_event.AddProperty("ts", timestamp); |
262 OS::SNPrint(&buff[0], kBuffSize-1, "Unknown [%" Px ", %" Px ")", | 497 { |
263 pc, pc + kBucketSize); | 498 JSONArray frames(&sample_event, "f"); |
264 symbol_name = strdup(buff); | 499 for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
265 *symbol_name_allocated = true; | 500 if (sample->pcs[i] == 0) { |
266 } | 501 continue; |
267 return symbol_name; | 502 } |
503 const char* symbol_name = symbol_helper->GetSymbol(sample->pcs[i]); | |
504 new_code ^= symbol_helper->GetNewCode(sample->pcs[i]); | |
505 if (!new_code.IsNull()) { | |
506 // Dump code. | |
507 frames.AddValue(new_code, false); | |
508 } | |
509 { | |
510 JSONObject tick(&frames); | |
511 tick.AddProperty("s", symbol_name); | |
512 tick.AddPropertyF("pc", "%" Px "", sample->pcs[i]); | |
513 } | |
514 } | |
515 } | |
516 } | |
268 } | 517 } |
269 | 518 |
270 | 519 |
271 void Profiler::WriteTracingSample(Isolate* isolate, intptr_t pid, | 520 void Profiler::WriteSamples(Isolate* isolate) { |
272 Sample* sample, JSONArray& events) { | |
273 Sample::SampleType type = sample->type; | |
274 intptr_t tid = Thread::ThreadIdToIntPtr(sample->tid); | |
275 double timestamp = static_cast<double>(sample->timestamp); | |
276 const char* isolate_name = isolate->name(); | |
277 switch (type) { | |
278 case Sample::kIsolateStart: { | |
279 JSONObject begin(&events); | |
280 begin.AddProperty("ph", "B"); | |
281 begin.AddProperty("tid", tid); | |
282 begin.AddProperty("pid", pid); | |
283 begin.AddProperty("name", isolate_name); | |
284 begin.AddProperty("ts", timestamp); | |
285 } | |
286 break; | |
287 case Sample::kIsolateStop: { | |
288 JSONObject begin(&events); | |
289 begin.AddProperty("ph", "E"); | |
290 begin.AddProperty("tid", tid); | |
291 begin.AddProperty("pid", pid); | |
292 begin.AddProperty("name", isolate_name); | |
293 begin.AddProperty("ts", timestamp); | |
294 } | |
295 break; | |
296 case Sample::kIsolateSample: | |
297 // Write "B" events. | |
298 for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { | |
299 bool symbol_name_allocated = false; | |
300 const char* symbol_name = FindSymbolName(sample->pcs[i], | |
301 &symbol_name_allocated); | |
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", timestamp); | |
309 } | |
310 if (symbol_name_allocated) { | |
311 free(const_cast<char*>(symbol_name)); | |
312 } | |
313 } | |
314 // Write "E" events. | |
315 for (int i = 0; i < Sample::kNumStackFrames; i++) { | |
316 bool symbol_name_allocated = false; | |
317 const char* symbol_name = FindSymbolName(sample->pcs[i], | |
318 &symbol_name_allocated); | |
319 { | |
320 JSONObject begin(&events); | |
321 begin.AddProperty("ph", "E"); | |
322 begin.AddProperty("tid", tid); | |
323 begin.AddProperty("pid", pid); | |
324 begin.AddProperty("name", symbol_name); | |
325 begin.AddProperty("ts", timestamp); | |
326 } | |
327 if (symbol_name_allocated) { | |
328 free(const_cast<char*>(symbol_name)); | |
329 } | |
330 } | |
331 break; | |
332 default: | |
333 UNIMPLEMENTED(); | |
334 } | |
335 } | |
336 | |
337 | |
338 void Profiler::WriteTracing(Isolate* isolate) { | |
339 if (isolate == NULL) { | 521 if (isolate == NULL) { |
340 return; | 522 return; |
341 } | 523 } |
342 if (!FLAG_profile) { | 524 if (!FLAG_profile) { |
343 return; | 525 return; |
344 } | 526 } |
345 ASSERT(initialized_); | 527 ASSERT(initialized_); |
346 if (FLAG_profile_dir == NULL) { | 528 if (FLAG_profile_dir == NULL) { |
347 return; | 529 return; |
348 } | 530 } |
349 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); | 531 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
350 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); | 532 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
351 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); | 533 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
352 if ((file_open == NULL) || (file_close == NULL) || (file_write == NULL)) { | 534 if ((file_open == NULL) || (file_close == NULL) || (file_write == NULL)) { |
353 // Embedder has not provided necessary callbacks. | 535 // Embedder has not provided necessary callbacks. |
354 return; | 536 return; |
355 } | 537 } |
356 // We will be looking up code objects within the isolate. | 538 // We will be looking up code objects within the isolate. |
357 ASSERT(Isolate::Current() != NULL); | 539 ASSERT(Isolate::Current() == isolate); |
358 // We do not want to be interrupted while processing the buffer. | |
359 EndExecution(isolate); | |
360 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | |
361 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
362 if (profiler_data == NULL) { | |
363 return; | |
364 } | |
365 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
366 ASSERT(sample_buffer != NULL); | |
367 JSONStream stream(10 * MB); | 540 JSONStream stream(10 * MB); |
368 intptr_t pid = OS::ProcessId(); | 541 intptr_t pid = OS::ProcessId(); |
369 { | 542 PrintToJSONStream(isolate, &stream); |
370 JSONArray events(&stream); | |
371 { | |
372 JSONObject process_name(&events); | |
373 process_name.AddProperty("name", "process_name"); | |
374 process_name.AddProperty("ph", "M"); | |
375 process_name.AddProperty("pid", pid); | |
376 { | |
377 JSONObject args(&process_name, "args"); | |
378 args.AddProperty("name", "Dart VM"); | |
379 } | |
380 } | |
381 for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { | |
382 Sample* sample = sample_buffer->GetSample(i); | |
383 if (sample->isolate != isolate) { | |
384 continue; | |
385 } | |
386 if (sample->timestamp == 0) { | |
387 continue; | |
388 } | |
389 WriteTracingSample(isolate, pid, sample, events); | |
390 } | |
391 } | |
392 const char* format = "%s/dart-profile-%" Pd "-%" Pd ".json"; | 543 const char* format = "%s/dart-profile-%" Pd "-%" Pd ".json"; |
393 intptr_t len = OS::SNPrint(NULL, 0, format, | 544 intptr_t len = OS::SNPrint(NULL, 0, format, |
394 FLAG_profile_dir, pid, isolate->main_port()); | 545 FLAG_profile_dir, pid, isolate->main_port()); |
395 char* filename = Isolate::Current()->current_zone()->Alloc<char>(len + 1); | 546 char* filename = Isolate::Current()->current_zone()->Alloc<char>(len + 1); |
396 OS::SNPrint(filename, len + 1, format, | 547 OS::SNPrint(filename, len + 1, format, |
397 FLAG_profile_dir, pid, isolate->main_port()); | 548 FLAG_profile_dir, pid, isolate->main_port()); |
398 void* f = file_open(filename, true); | 549 void* f = file_open(filename, true); |
399 if (f == NULL) { | 550 if (f == NULL) { |
400 // Cannot write. | 551 // Cannot write. |
401 return; | 552 return; |
402 } | 553 } |
403 TextBuffer* buffer = stream.buffer(); | 554 TextBuffer* buffer = stream.buffer(); |
404 ASSERT(buffer != NULL); | 555 ASSERT(buffer != NULL); |
405 file_write(buffer->buf(), buffer->length(), f); | 556 file_write(buffer->buf(), buffer->length(), f); |
406 file_close(f); | 557 file_close(f); |
407 BeginExecution(isolate); | |
408 } | 558 } |
409 | 559 |
410 | 560 |
411 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, | 561 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, |
412 bool own_sample_buffer) { | 562 bool own_sample_buffer) { |
413 sample_buffer_ = sample_buffer; | 563 sample_buffer_ = sample_buffer; |
414 own_sample_buffer_ = own_sample_buffer; | 564 own_sample_buffer_ = own_sample_buffer; |
415 } | 565 } |
416 | 566 |
417 | 567 |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
550 return false; | 700 return false; |
551 } | 701 } |
552 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); | 702 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); |
553 cursor += sizeof(fp); | 703 cursor += sizeof(fp); |
554 bool r = cursor >= lower_bound_ && cursor < stack_upper_; | 704 bool r = cursor >= lower_bound_ && cursor < stack_upper_; |
555 return r; | 705 return r; |
556 } | 706 } |
557 | 707 |
558 | 708 |
559 } // namespace dart | 709 } // namespace dart |
OLD | NEW |