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 // Size of entries in both block_ids and counts.
63 constexpr int kBasicBlockSlotSize = kInt32Size;
64 } // namespace
65
BasicBlockProfilerData(Handle<OnHeapBasicBlockProfilerData> js_heap_data,Isolate * isolate)66 BasicBlockProfilerData::BasicBlockProfilerData(
67 Handle<OnHeapBasicBlockProfilerData> js_heap_data, Isolate* isolate) {
68 function_name_ = js_heap_data->name().ToCString().get();
69 schedule_ = js_heap_data->schedule().ToCString().get();
70 code_ = js_heap_data->code().ToCString().get();
71 Handle<ByteArray> counts(js_heap_data->counts(), isolate);
72 for (int i = 0; i < counts->length() / kBasicBlockSlotSize; ++i) {
73 counts_.push_back(counts->get_uint32(i));
74 }
75 Handle<ByteArray> block_ids(js_heap_data->block_ids(), isolate);
76 for (int i = 0; i < block_ids->length() / kBasicBlockSlotSize; ++i) {
77 block_ids_.push_back(block_ids->get_int(i));
78 }
79 CHECK_EQ(block_ids_.size(), counts_.size());
80 hash_ = js_heap_data->hash();
81 }
82
BasicBlockProfilerData(OnHeapBasicBlockProfilerData js_heap_data)83 BasicBlockProfilerData::BasicBlockProfilerData(
84 OnHeapBasicBlockProfilerData js_heap_data) {
85 function_name_ = js_heap_data.name().ToCString().get();
86 schedule_ = js_heap_data.schedule().ToCString().get();
87 code_ = js_heap_data.code().ToCString().get();
88 ByteArray counts(js_heap_data.counts());
89 for (int i = 0; i < counts.length() / kBasicBlockSlotSize; ++i) {
90 counts_.push_back(counts.get_uint32(i));
91 }
92 ByteArray block_ids(js_heap_data.block_ids());
93 for (int i = 0; i < block_ids.length() / kBasicBlockSlotSize; ++i) {
94 block_ids_.push_back(block_ids.get_int(i));
95 }
96 CHECK_EQ(block_ids_.size(), counts_.size());
97 }
98
CopyToJSHeap(Isolate * isolate)99 Handle<OnHeapBasicBlockProfilerData> BasicBlockProfilerData::CopyToJSHeap(
100 Isolate* isolate) {
101 int array_size_in_bytes = static_cast<int>(n_blocks() * kBasicBlockSlotSize);
102 CHECK(array_size_in_bytes >= 0 &&
103 static_cast<size_t>(array_size_in_bytes) / kBasicBlockSlotSize ==
104 n_blocks()); // Overflow
105 Handle<ByteArray> block_ids = isolate->factory()->NewByteArray(
106 array_size_in_bytes, AllocationType::kOld);
107 for (int i = 0; i < static_cast<int>(n_blocks()); ++i) {
108 block_ids->set_int(i, block_ids_[i]);
109 }
110 Handle<ByteArray> counts = isolate->factory()->NewByteArray(
111 array_size_in_bytes, AllocationType::kOld);
112 for (int i = 0; i < static_cast<int>(n_blocks()); ++i) {
113 counts->set_uint32(i, counts_[i]);
114 }
115 Handle<String> name = CopyStringToJSHeap(function_name_, isolate);
116 Handle<String> schedule = CopyStringToJSHeap(schedule_, isolate);
117 Handle<String> code = CopyStringToJSHeap(code_, isolate);
118
119 return isolate->factory()->NewOnHeapBasicBlockProfilerData(
120 block_ids, counts, name, schedule, code, hash_, AllocationType::kOld);
121 }
122
ResetCounts(Isolate * isolate)123 void BasicBlockProfiler::ResetCounts(Isolate* isolate) {
124 for (const auto& data : data_list_) {
125 data->ResetCounts();
126 }
127 HandleScope scope(isolate);
128 Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
129 isolate);
130 for (int i = 0; i < list->Length(); ++i) {
131 Handle<ByteArray> counts(
132 OnHeapBasicBlockProfilerData::cast(list->Get(i)).counts(), isolate);
133 for (int j = 0; j < counts->length() / kBasicBlockSlotSize; ++j) {
134 counts->set_uint32(j, 0);
135 }
136 }
137 }
138
HasData(Isolate * isolate)139 bool BasicBlockProfiler::HasData(Isolate* isolate) {
140 return data_list_.size() > 0 ||
141 isolate->heap()->basic_block_profiling_data().Length() > 0;
142 }
143
Print(std::ostream & os,Isolate * isolate)144 void BasicBlockProfiler::Print(std::ostream& os, Isolate* isolate) {
145 os << "---- Start Profiling Data ----" << std::endl;
146 for (const auto& data : data_list_) {
147 os << *data;
148 }
149 HandleScope scope(isolate);
150 Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
151 isolate);
152 std::unordered_set<std::string> builtin_names;
153 for (int i = 0; i < list->Length(); ++i) {
154 BasicBlockProfilerData data(
155 handle(OnHeapBasicBlockProfilerData::cast(list->Get(i)), isolate),
156 isolate);
157 // Print data for builtins to both stdout and the log file, if logging is
158 // enabled.
159 os << data;
160 data.Log(isolate);
161 // Ensure that all builtin names are unique; otherwise profile-guided
162 // optimization might get confused.
163 CHECK(builtin_names.insert(data.function_name_).second);
164 }
165 os << "---- End Profiling Data ----" << std::endl;
166 }
167
GetCoverageBitmap(Isolate * isolate)168 std::vector<bool> BasicBlockProfiler::GetCoverageBitmap(Isolate* isolate) {
169 DisallowHeapAllocation no_gc;
170 ArrayList list(isolate->heap()->basic_block_profiling_data());
171 std::vector<bool> out;
172 int list_length = list.Length();
173 for (int i = 0; i < list_length; ++i) {
174 BasicBlockProfilerData data(
175 OnHeapBasicBlockProfilerData::cast(list.Get(i)));
176 for (size_t i = 0; i < data.n_blocks(); ++i) {
177 out.push_back(data.counts_[i] > 0);
178 }
179 }
180 return out;
181 }
182
Log(Isolate * isolate)183 void BasicBlockProfilerData::Log(Isolate* isolate) {
184 bool any_nonzero_counter = false;
185 for (size_t i = 0; i < n_blocks(); ++i) {
186 if (counts_[i] > 0) {
187 any_nonzero_counter = true;
188 isolate->logger()->BasicBlockCounterEvent(function_name_.c_str(),
189 block_ids_[i], counts_[i]);
190 }
191 }
192 if (any_nonzero_counter) {
193 isolate->logger()->BuiltinHashEvent(function_name_.c_str(), hash_);
194 }
195 }
196
operator <<(std::ostream & os,const BasicBlockProfilerData & d)197 std::ostream& operator<<(std::ostream& os, const BasicBlockProfilerData& d) {
198 int block_count_sum = std::accumulate(d.counts_.begin(), d.counts_.end(), 0);
199 if (block_count_sum == 0) return os;
200 const char* name = "unknown function";
201 if (!d.function_name_.empty()) {
202 name = d.function_name_.c_str();
203 }
204 if (!d.schedule_.empty()) {
205 os << "schedule for " << name << " (B0 entered " << d.counts_[0]
206 << " times)" << std::endl;
207 os << d.schedule_.c_str() << std::endl;
208 }
209 os << "block counts for " << name << ":" << std::endl;
210 std::vector<std::pair<size_t, uint32_t>> pairs;
211 pairs.reserve(d.n_blocks());
212 for (size_t i = 0; i < d.n_blocks(); ++i) {
213 pairs.push_back(std::make_pair(i, d.counts_[i]));
214 }
215 std::sort(
216 pairs.begin(), pairs.end(),
217 [=](std::pair<size_t, uint32_t> left, std::pair<size_t, uint32_t> right) {
218 if (right.second == left.second) return left.first < right.first;
219 return right.second < left.second;
220 });
221 for (auto it : pairs) {
222 if (it.second == 0) break;
223 os << "block B" << it.first << " : " << it.second << std::endl;
224 }
225 os << std::endl;
226 if (!d.code_.empty()) {
227 os << d.code_.c_str() << std::endl;
228 }
229 return os;
230 }
231
232 } // namespace internal
233 } // namespace v8
234