Index: runtime/vm/code_statistics.cc |
diff --git a/runtime/vm/code_statistics.cc b/runtime/vm/code_statistics.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..906a4d049c11cf00055af9600dc360deb2af9da1 |
--- /dev/null |
+++ b/runtime/vm/code_statistics.cc |
@@ -0,0 +1,258 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#include "vm/code_statistics.h" |
+ |
+namespace dart { |
+ |
+CombinedCodeStatistics::CombinedCodeStatistics() { |
+ unaccounted_bytes_ = 0; |
+ alignment_bytes_ = 0; |
+ object_header_bytes_ = 0; |
+ wasteful_function_count_ = 0; |
+ return_const_count_ = 0; |
+ return_const_with_load_field_count_ = 0; |
+ intptr_t i = 0; |
+ |
+#define DO(type) \ |
+ entries_[i].name = #type; \ |
+ entries_[i].bytes = 0; \ |
+ entries_[i++].count = 0; |
+ |
+ FOR_EACH_INSTRUCTION(DO) |
+ |
+#undef DO |
+ |
+#define INIT_SPECIAL_ENTRY(tag, str) \ |
+ entries_[tag].name = str; \ |
+ entries_[tag].bytes = 0; \ |
+ entries_[tag].count = 0; |
+ |
+ INIT_SPECIAL_ENTRY(kPolymorphicInstanceCallAsStaticCall, |
+ "PolymorphicInstanceCall -> StaticCall") |
+ |
+ INIT_SPECIAL_ENTRY(kTagCheckedSmiSlowPath, "<slow-path:checked-smi>") |
+ INIT_SPECIAL_ENTRY(kTagCheckedSmiCmpSlowPath, "<slow-path:checked-smi-compare>") |
+ INIT_SPECIAL_ENTRY(kTagBoxAllocationSlowPath, "<slow-path:box-allocation>") |
+ INIT_SPECIAL_ENTRY(kTagAllocateContextSlowPath, |
+ "<slow-path:allocate-context>") |
+ INIT_SPECIAL_ENTRY(kTagCheckStackOverflowSlowPath, |
+ "<slow-path:stack-overflow>") |
+ INIT_SPECIAL_ENTRY(kTagMegamorphicSlowPath, "<slow-path:megamorphic>") |
+ |
+ INIT_SPECIAL_ENTRY(kTagCheckArgumentCount, "<check argument count>"); |
+ INIT_SPECIAL_ENTRY(kTagCopyParameters, "<copy parameters>"); |
+ INIT_SPECIAL_ENTRY(kTagStubCode, "<stub-code>"); |
+ INIT_SPECIAL_ENTRY(kTagCheckedEntry, "<checked-entry-prologue>"); |
+ INIT_SPECIAL_ENTRY(kTagFrameEntry, "<frame-entry>"); |
+ INIT_SPECIAL_ENTRY(kTagLoadClosureContext, "<load-closure-context>"); |
+ INIT_SPECIAL_ENTRY(kTagIntrinsics, "<intrinsics>"); |
+ INIT_SPECIAL_ENTRY(kDebugAfterBody, "<debug-after-body>"); |
+ |
+ INIT_SPECIAL_ENTRY(kTagTrySyncSpilling, "<try-sync-spilling-code>"); |
+ |
+#undef INIT_SPECIAL_ENTRY |
+} |
+ |
+void CombinedCodeStatistics::DumpStatistics() { |
+ ASSERT(unaccounted_bytes_ >= 0); |
+ |
+ SlowSort(); |
+ |
+ intptr_t instruction_bytes = 0; |
+ for (intptr_t i = 0; i < kNumEntries; i++) { |
+ instruction_bytes += entries_[i].bytes; |
+ } |
+ intptr_t total = object_header_bytes_ + |
+ instruction_bytes + |
+ unaccounted_bytes_ + |
+ alignment_bytes_; |
+ float ftotal = static_cast<float>(total) / 100.0; |
+ |
+ fprintf(stderr, "--------------------\n"); |
+ |
+ for (intptr_t i = 0; i < kNumEntries; i++) { |
+ const char* name = entries_[i].name; |
+ intptr_t bytes = entries_[i].bytes; |
+ intptr_t count = entries_[i].count; |
+ float percent = bytes / ftotal; |
+ float avg = static_cast<float>(bytes) / count; |
+ if (bytes > 0) { |
+ fprintf( |
+ stderr, |
+ "%5.2f %% " |
+ "% 8" Pd " bytes " |
+ "% 8" Pd " count " |
+ "%8.2f avg bytes/entry " |
+ "- %s\n", |
+ percent, |
+ bytes, |
+ count, |
+ avg, |
+ name); |
+ } |
+ } |
+ |
+ fprintf(stderr, "--------------------\n"); |
+ |
+ fprintf(stderr, "%5.2f %% % 8" Pd " bytes unaccounted\n", |
+ unaccounted_bytes_/ftotal, unaccounted_bytes_); |
+ fprintf(stderr, "%5.2f %% % 8" Pd " bytes alignment\n", |
+ alignment_bytes_ / ftotal, alignment_bytes_); |
+ fprintf(stderr, "%5.2f %% % 8" Pd " bytes instruction object header\n", |
+ object_header_bytes_ / ftotal, object_header_bytes_); |
+ fprintf(stderr, "%5.2f %% % 8" Pd " bytes instructions\n", |
+ instruction_bytes / ftotal, instruction_bytes); |
+ fprintf(stderr, "--------------------\n"); |
+ fprintf(stderr, "%5.2f %% % 8" Pd " bytes in total\n", |
+ total/ftotal, total); |
+ fprintf(stderr, "--------------------\n"); |
+ fprintf(stderr, "% 8" Pd " return-constant functions\n", return_const_count_); |
+ fprintf(stderr, "% 8" Pd " return-constant-with-load-field functions\n", |
+ return_const_with_load_field_count_); |
+ fprintf(stderr, "% 8" Pd " wasteful functions (body < 2 * frame overhead)\n", |
+ wasteful_function_count_); |
+ fprintf(stderr, "--------------------\n"); |
+} |
+ |
+ |
+void CombinedCodeStatistics::SlowSort() { |
+ for (intptr_t upper = kNumEntries - 1; upper >= 0; upper--) { |
+ intptr_t largest_index = 0; |
+ intptr_t largest_value = entries_[largest_index].bytes; |
+ for (intptr_t i = 1; i <= upper; i++) { |
+ intptr_t bytes = entries_[i].bytes; |
+ if (largest_value < bytes) { |
+ largest_index = i; |
+ largest_value = bytes; |
+ } |
+ } |
+ if (largest_index != upper) Swap(largest_index, upper); |
+ } |
+} |
+ |
+void CombinedCodeStatistics::Swap(intptr_t a, intptr_t b) { |
+ const char* a_name = entries_[a].name; |
+ intptr_t a_bytes = entries_[a].bytes; |
+ intptr_t a_count = entries_[a].count; |
+ |
+ entries_[a].name = entries_[b].name; |
+ entries_[a].bytes = entries_[b].bytes; |
+ entries_[a].count = entries_[b].count; |
+ |
+ entries_[b].name = a_name; |
+ entries_[b].bytes = a_bytes; |
+ entries_[b].count = a_count; |
+} |
+ |
+CodeStatistics::CodeStatistics(Assembler* assembler) |
+ : assembler_(assembler) { |
+ memset(entries_, 0, CombinedCodeStatistics::kNumEntries * sizeof(Entry)); |
+ instruction_bytes_ = 0; |
+ unaccounted_bytes_ = 0; |
+ alignment_bytes_ = 0; |
+ |
+ stack_index_ = -1; |
+ for (intptr_t i = 0; i < kStackSize; i++) stack_[i] = -1; |
+} |
+ |
+void CodeStatistics::Begin(Instruction* instruction) { |
+ SpecialBegin(static_cast<intptr_t>(instruction->tag())); |
+} |
+ |
+void CodeStatistics::End(Instruction* instruction) { |
+ SpecialEnd(static_cast<intptr_t>(instruction->tag())); |
+} |
+ |
+void CodeStatistics::SpecialBegin(intptr_t tag) { |
+ stack_index_++; |
+ ASSERT(stack_index_ < kStackSize); |
+ ASSERT(stack_[stack_index_] == -1); |
+ ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
+ stack_[stack_index_] = assembler_->CodeSize(); |
+ ASSERT(stack_[stack_index_] >= 0); |
+} |
+ |
+void CodeStatistics::SpecialEnd(intptr_t tag) { |
+ ASSERT(stack_index_ > 0 || stack_[stack_index_] >= 0); |
+ ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
+ |
+ intptr_t diff = assembler_->CodeSize() - stack_[stack_index_]; |
+ ASSERT(diff >= 0); |
+ ASSERT(entries_[tag].bytes >= 0); |
+ ASSERT(entries_[tag].count >= 0); |
+ entries_[tag].bytes += diff; |
+ entries_[tag].count++; |
+ instruction_bytes_ += diff; |
+ stack_[stack_index_] = -1; |
+ stack_index_--; |
+ |
+ // By adding to the current stack index we will increase the 'assembler_->CodeSize()' offset |
+ // when the parent instruction started, thereby reducing it's accounted size |
+ // by `diff`. |
+ if (tag == CombinedCodeStatistics::kTagTrySyncSpilling) { |
+ // We make try-sync-spilling code be subtracted from the calls. |
+ ASSERT(stack_index_ >= 0); |
+ stack_[stack_index_] += diff; |
+ } else if (tag == CombinedCodeStatistics::kPolymorphicInstanceCallAsStaticCall) { |
+ // We make polymorphic-as-static-calls be subtraced from the polymorphic |
+ // calls. |
+ ASSERT(stack_index_ >= 0); |
+ stack_[stack_index_] += diff; |
+ } |
+} |
+ |
+void CodeStatistics::Finalize() { |
+ intptr_t function_size = assembler_->CodeSize(); |
+ unaccounted_bytes_ = function_size - instruction_bytes_; |
+ ASSERT(unaccounted_bytes_ >= 0); |
+ alignment_bytes_ = |
+ Utils::RoundUp(function_size, OS::PreferredCodeAlignment()) - |
+ function_size; |
+ assembler_ = NULL; |
+} |
+ |
+void CodeStatistics::AppendTo(CombinedCodeStatistics* stat) { |
+ intptr_t sum = 0; |
+ bool returns_constant = true; |
+ bool returns_const_with_load_field_ = true; |
+ |
+ for (intptr_t i = 0; i < CombinedCodeStatistics::kNumEntries; i++) { |
+ intptr_t bytes = entries_[i].bytes; |
+ stat->entries_[i].count += entries_[i].count; |
+ if (bytes > 0) { |
+ sum += bytes; |
+ stat->entries_[i].bytes += bytes; |
+ if (i != CombinedCodeStatistics::kTagFrameEntry && |
+ i != CombinedCodeStatistics::kTagParallelMove && |
+ i != CombinedCodeStatistics::kTagReturn && |
+ i != CombinedCodeStatistics::kTagCheckStackOverflow && |
+ i != CombinedCodeStatistics::kTagCheckStackOverflowSlowPath) { |
+ returns_constant = false; |
+ if (i != CombinedCodeStatistics::kTagLoadField && |
+ i != CombinedCodeStatistics::kTagTargetEntry && |
+ i != CombinedCodeStatistics::kTagJoinEntry) { |
+ returns_const_with_load_field_ = false; |
+ } |
+ } |
+ } |
+ } |
+ stat->unaccounted_bytes_ += unaccounted_bytes_; |
+ ASSERT(stat->unaccounted_bytes_ >= 0); |
+ stat->alignment_bytes_ += alignment_bytes_; |
+ stat->object_header_bytes_ += Instructions::HeaderSize(); |
+ |
+ intptr_t frame_overhead = |
+ entries_[CombinedCodeStatistics::kTagFrameEntry].bytes + |
+ entries_[CombinedCodeStatistics::kTagReturn].bytes; |
+ |
+ bool is_wasteful = sum < (2 * frame_overhead); |
+ if (is_wasteful) stat->wasteful_function_count_++; |
+ if (returns_constant) stat->return_const_count_++; |
+ if (returns_const_with_load_field_) { |
+ stat->return_const_with_load_field_count_++; |
+ } |
+} |
+ |
+} // namespace dart |