• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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