1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/diagnostics/basic-block-profiler.h"
6
7 #include <algorithm>
8 #include <numeric>
9 #include <sstream>
10
11 #include "src/base/lazy-instance.h"
12 #include "src/heap/heap-inl.h"
13 #include "src/objects/shared-function-info-inl.h"
14
15 namespace v8 {
16 namespace internal {
17
DEFINE_LAZY_LEAKY_OBJECT_GETTER(BasicBlockProfiler,BasicBlockProfiler::Get)18 DEFINE_LAZY_LEAKY_OBJECT_GETTER(BasicBlockProfiler, BasicBlockProfiler::Get)
19
20 BasicBlockProfilerData::BasicBlockProfilerData(size_t n_blocks)
21 : block_ids_(n_blocks), counts_(n_blocks, 0) {}
22
SetCode(const std::ostringstream & os)23 void BasicBlockProfilerData::SetCode(const std::ostringstream& os) {
24 code_ = os.str();
25 }
26
SetFunctionName(std::unique_ptr<char[]> name)27 void BasicBlockProfilerData::SetFunctionName(std::unique_ptr<char[]> name) {
28 function_name_ = name.get();
29 }
30
SetSchedule(const std::ostringstream & os)31 void BasicBlockProfilerData::SetSchedule(const std::ostringstream& os) {
32 schedule_ = os.str();
33 }
34
SetBlockId(size_t offset,int32_t id)35 void BasicBlockProfilerData::SetBlockId(size_t offset, int32_t id) {
36 DCHECK(offset < n_blocks());
37 block_ids_[offset] = id;
38 }
39
SetHash(int hash)40 void BasicBlockProfilerData::SetHash(int hash) { hash_ = hash; }
41
ResetCounts()42 void BasicBlockProfilerData::ResetCounts() {
43 for (size_t i = 0; i < n_blocks(); ++i) {
44 counts_[i] = 0;
45 }
46 }
47
NewData(size_t n_blocks)48 BasicBlockProfilerData* BasicBlockProfiler::NewData(size_t n_blocks) {
49 base::MutexGuard lock(&data_list_mutex_);
50 auto data = std::make_unique<BasicBlockProfilerData>(n_blocks);
51 BasicBlockProfilerData* data_ptr = data.get();
52 data_list_.push_back(std::move(data));
53 return data_ptr;
54 }
55
56 namespace {
CopyStringToJSHeap(const std::string & source,Isolate * isolate)57 Handle<String> CopyStringToJSHeap(const std::string& source, Isolate* isolate) {
58 return isolate->factory()->NewStringFromAsciiChecked(source.c_str(),
59 AllocationType::kOld);
60 }
61
62 constexpr int kBlockIdSlotSize = kInt32Size;
63 constexpr int kBlockCountSlotSize = kInt32Size;
64 } // namespace
65
BasicBlockProfilerData(Handle<OnHeapBasicBlockProfilerData> js_heap_data,Isolate * isolate)66 BasicBlockProfilerData::BasicBlockProfilerData(
67 Handle<OnHeapBasicBlockProfilerData> js_heap_data, Isolate* isolate) {
68 DisallowHeapAllocation no_gc;
69 CopyFromJSHeap(*js_heap_data);
70 }
71
BasicBlockProfilerData(OnHeapBasicBlockProfilerData js_heap_data)72 BasicBlockProfilerData::BasicBlockProfilerData(
73 OnHeapBasicBlockProfilerData js_heap_data) {
74 CopyFromJSHeap(js_heap_data);
75 }
76
CopyFromJSHeap(OnHeapBasicBlockProfilerData js_heap_data)77 void BasicBlockProfilerData::CopyFromJSHeap(
78 OnHeapBasicBlockProfilerData js_heap_data) {
79 function_name_ = js_heap_data.name().ToCString().get();
80 schedule_ = js_heap_data.schedule().ToCString().get();
81 code_ = js_heap_data.code().ToCString().get();
82 ByteArray counts(js_heap_data.counts());
83 for (int i = 0; i < counts.length() / kBlockCountSlotSize; ++i) {
84 counts_.push_back(counts.get_uint32(i));
85 }
86 ByteArray block_ids(js_heap_data.block_ids());
87 for (int i = 0; i < block_ids.length() / kBlockIdSlotSize; ++i) {
88 block_ids_.push_back(block_ids.get_int(i));
89 }
90 CHECK_EQ(block_ids_.size(), counts_.size());
91 hash_ = js_heap_data.hash();
92 }
93
CopyToJSHeap(Isolate * isolate)94 Handle<OnHeapBasicBlockProfilerData> BasicBlockProfilerData::CopyToJSHeap(
95 Isolate* isolate) {
96 int id_array_size_in_bytes = static_cast<int>(n_blocks() * kBlockIdSlotSize);
97 CHECK(id_array_size_in_bytes >= 0 &&
98 static_cast<size_t>(id_array_size_in_bytes) / kBlockIdSlotSize ==
99 n_blocks()); // Overflow
100 Handle<ByteArray> block_ids = isolate->factory()->NewByteArray(
101 id_array_size_in_bytes, AllocationType::kOld);
102 for (int i = 0; i < static_cast<int>(n_blocks()); ++i) {
103 block_ids->set_int(i, block_ids_[i]);
104 }
105
106 int counts_array_size_in_bytes =
107 static_cast<int>(n_blocks() * kBlockCountSlotSize);
108 CHECK(counts_array_size_in_bytes >= 0 &&
109 static_cast<size_t>(counts_array_size_in_bytes) / kBlockCountSlotSize ==
110 n_blocks()); // Overflow
111 Handle<ByteArray> counts = isolate->factory()->NewByteArray(
112 counts_array_size_in_bytes, AllocationType::kOld);
113 for (int i = 0; i < static_cast<int>(n_blocks()); ++i) {
114 counts->set_uint32(i, counts_[i]);
115 }
116 Handle<String> name = CopyStringToJSHeap(function_name_, isolate);
117 Handle<String> schedule = CopyStringToJSHeap(schedule_, isolate);
118 Handle<String> code = CopyStringToJSHeap(code_, isolate);
119
120 return isolate->factory()->NewOnHeapBasicBlockProfilerData(
121 block_ids, counts, name, schedule, code, hash_, AllocationType::kOld);
122 }
123
ResetCounts(Isolate * isolate)124 void BasicBlockProfiler::ResetCounts(Isolate* isolate) {
125 for (const auto& data : data_list_) {
126 data->ResetCounts();
127 }
128 HandleScope scope(isolate);
129 Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
130 isolate);
131 for (int i = 0; i < list->Length(); ++i) {
132 Handle<ByteArray> counts(
133 OnHeapBasicBlockProfilerData::cast(list->Get(i)).counts(), isolate);
134 for (int j = 0; j < counts->length() / kBlockCountSlotSize; ++j) {
135 counts->set_uint32(j, 0);
136 }
137 }
138 }
139
HasData(Isolate * isolate)140 bool BasicBlockProfiler::HasData(Isolate* isolate) {
141 return data_list_.size() > 0 ||
142 isolate->heap()->basic_block_profiling_data().Length() > 0;
143 }
144
Print(std::ostream & os,Isolate * isolate)145 void BasicBlockProfiler::Print(std::ostream& os, Isolate* isolate) {
146 os << "---- Start Profiling Data ----" << std::endl;
147 for (const auto& data : data_list_) {
148 os << *data;
149 }
150 HandleScope scope(isolate);
151 Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
152 isolate);
153 std::unordered_set<std::string> builtin_names;
154 for (int i = 0; i < list->Length(); ++i) {
155 BasicBlockProfilerData data(
156 handle(OnHeapBasicBlockProfilerData::cast(list->Get(i)), isolate),
157 isolate);
158 // Print data for builtins to both stdout and the log file, if logging is
159 // enabled.
160 os << data;
161 data.Log(isolate);
162 // Ensure that all builtin names are unique; otherwise profile-guided
163 // optimization might get confused.
164 CHECK(builtin_names.insert(data.function_name_).second);
165 }
166 os << "---- End Profiling Data ----" << std::endl;
167 }
168
GetCoverageBitmap(Isolate * isolate)169 std::vector<bool> BasicBlockProfiler::GetCoverageBitmap(Isolate* isolate) {
170 DisallowGarbageCollection no_gc;
171 ArrayList list(isolate->heap()->basic_block_profiling_data());
172 std::vector<bool> out;
173 int list_length = list.Length();
174 for (int i = 0; i < list_length; ++i) {
175 BasicBlockProfilerData data(
176 OnHeapBasicBlockProfilerData::cast(list.Get(i)));
177 for (size_t j = 0; j < data.n_blocks(); ++j) {
178 out.push_back(data.counts_[j] > 0);
179 }
180 }
181 return out;
182 }
183
Log(Isolate * isolate)184 void BasicBlockProfilerData::Log(Isolate* isolate) {
185 bool any_nonzero_counter = false;
186 for (size_t i = 0; i < n_blocks(); ++i) {
187 if (counts_[i] > 0) {
188 any_nonzero_counter = true;
189 isolate->logger()->BasicBlockCounterEvent(function_name_.c_str(),
190 block_ids_[i], counts_[i]);
191 }
192 }
193 if (any_nonzero_counter) {
194 isolate->logger()->BuiltinHashEvent(function_name_.c_str(), hash_);
195 }
196 }
197
operator <<(std::ostream & os,const BasicBlockProfilerData & d)198 std::ostream& operator<<(std::ostream& os, const BasicBlockProfilerData& d) {
199 if (std::all_of(d.counts_.cbegin(), d.counts_.cend(),
200 [](uint32_t count) { return count == 0; })) {
201 // No data was collected for this function.
202 return os;
203 }
204 const char* name = "unknown function";
205 if (!d.function_name_.empty()) {
206 name = d.function_name_.c_str();
207 }
208 if (!d.schedule_.empty()) {
209 os << "schedule for " << name << " (B0 entered " << d.counts_[0]
210 << " times)" << std::endl;
211 os << d.schedule_.c_str() << std::endl;
212 }
213 os << "block counts for " << name << ":" << std::endl;
214 std::vector<std::pair<size_t, uint32_t>> pairs;
215 pairs.reserve(d.n_blocks());
216 for (size_t i = 0; i < d.n_blocks(); ++i) {
217 pairs.push_back(std::make_pair(i, d.counts_[i]));
218 }
219 std::sort(
220 pairs.begin(), pairs.end(),
221 [=](std::pair<size_t, uint32_t> left, std::pair<size_t, uint32_t> right) {
222 if (right.second == left.second) return left.first < right.first;
223 return right.second < left.second;
224 });
225 for (auto it : pairs) {
226 if (it.second == 0) break;
227 os << "block B" << it.first << " : " << it.second << std::endl;
228 }
229 os << std::endl;
230 if (!d.code_.empty()) {
231 os << d.code_.c_str() << std::endl;
232 }
233 return os;
234 }
235
236 } // namespace internal
237 } // namespace v8
238