basic-block-profiler.cc 7.95 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/diagnostics/basic-block-profiler.h"
6

7 8
#include <algorithm>
#include <numeric>
9 10
#include <sstream>

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 16 17
namespace v8 {
namespace internal {

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

23 24
void BasicBlockProfilerData::SetCode(const std::ostringstream& os) {
  code_ = os.str();
25 26
}

27 28
void BasicBlockProfilerData::SetFunctionName(std::unique_ptr<char[]> name) {
  function_name_ = name.get();
29
}
30

31 32
void BasicBlockProfilerData::SetSchedule(const std::ostringstream& os) {
  schedule_ = os.str();
33 34
}

35
void BasicBlockProfilerData::SetBlockId(size_t offset, int32_t id) {
36
  DCHECK(offset < n_blocks());
37
  block_ids_[offset] = id;
38 39
}

40 41
void BasicBlockProfilerData::SetHash(int hash) { hash_ = hash; }

42 43 44 45
void BasicBlockProfilerData::ResetCounts() {
  for (size_t i = 0; i < n_blocks(); ++i) {
    counts_[i] = 0;
  }
46 47
}

48 49 50 51 52 53
BasicBlockProfilerData* BasicBlockProfiler::NewData(size_t n_blocks) {
  base::MutexGuard lock(&data_list_mutex_);
  auto data = std::make_unique<BasicBlockProfilerData>(n_blocks);
  BasicBlockProfilerData* data_ptr = data.get();
  data_list_.push_back(std::move(data));
  return data_ptr;
54 55
}

56 57 58 59
namespace {
Handle<String> CopyStringToJSHeap(const std::string& source, Isolate* isolate) {
  return isolate->factory()->NewStringFromAsciiChecked(source.c_str(),
                                                       AllocationType::kOld);
60 61
}

62
constexpr int kBlockIdSlotSize = kInt32Size;
63
constexpr int kBlockCountSlotSize = kInt32Size;
64 65 66 67
}  // namespace

BasicBlockProfilerData::BasicBlockProfilerData(
    Handle<OnHeapBasicBlockProfilerData> js_heap_data, Isolate* isolate) {
68 69
  DisallowHeapAllocation no_gc;
  CopyFromJSHeap(*js_heap_data);
70 71
}

72 73
BasicBlockProfilerData::BasicBlockProfilerData(
    OnHeapBasicBlockProfilerData js_heap_data) {
74 75 76 77 78
  CopyFromJSHeap(js_heap_data);
}

void BasicBlockProfilerData::CopyFromJSHeap(
    OnHeapBasicBlockProfilerData js_heap_data) {
79 80 81 82
  function_name_ = js_heap_data.name().ToCString().get();
  schedule_ = js_heap_data.schedule().ToCString().get();
  code_ = js_heap_data.code().ToCString().get();
  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
}

94 95
Handle<OnHeapBasicBlockProfilerData> BasicBlockProfilerData::CopyToJSHeap(
    Isolate* isolate) {
96 97 98
  int id_array_size_in_bytes = static_cast<int>(n_blocks() * kBlockIdSlotSize);
  CHECK(id_array_size_in_bytes >= 0 &&
        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 107 108 109 110

  int counts_array_size_in_bytes =
      static_cast<int>(n_blocks() * kBlockCountSlotSize);
  CHECK(counts_array_size_in_bytes >= 0 &&
        static_cast<size_t>(counts_array_size_in_bytes) / kBlockCountSlotSize ==
            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 117 118 119 120
  }
  Handle<String> name = CopyStringToJSHeap(function_name_, isolate);
  Handle<String> schedule = CopyStringToJSHeap(schedule_, isolate);
  Handle<String> code = CopyStringToJSHeap(code_, isolate);

  return isolate->factory()->NewOnHeapBasicBlockProfilerData(
121
      block_ids, counts, name, schedule, code, hash_, AllocationType::kOld);
122 123
}

124 125 126 127
void BasicBlockProfiler::ResetCounts(Isolate* isolate) {
  for (const auto& data : data_list_) {
    data->ResetCounts();
  }
128
  HandleScope scope(isolate);
129 130 131 132 133
  Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
                         isolate);
  for (int i = 0; i < list->Length(); ++i) {
    Handle<ByteArray> counts(
        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
  }
}

140 141 142 143 144
bool BasicBlockProfiler::HasData(Isolate* isolate) {
  return data_list_.size() > 0 ||
         isolate->heap()->basic_block_profiling_data().Length() > 0;
}

145
void BasicBlockProfiler::Print(std::ostream& os, Isolate* isolate) {
146
  os << "---- Start Profiling Data ----" << std::endl;
147 148 149 150 151 152
  for (const auto& data : data_list_) {
    os << *data;
  }
  HandleScope scope(isolate);
  Handle<ArrayList> list(isolate->heap()->basic_block_profiling_data(),
                         isolate);
153
  std::unordered_set<std::string> builtin_names;
154 155 156 157
  for (int i = 0; i < list->Length(); ++i) {
    BasicBlockProfilerData data(
        handle(OnHeapBasicBlockProfilerData::cast(list->Get(i)), isolate),
        isolate);
158 159
    // Print data for builtins to both stdout and the log file, if logging is
    // enabled.
160
    os << data;
161 162 163 164
    data.Log(isolate);
    // Ensure that all builtin names are unique; otherwise profile-guided
    // optimization might get confused.
    CHECK(builtin_names.insert(data.function_name_).second);
165
  }
166
  os << "---- End Profiling Data ----" << std::endl;
167 168
}

169
std::vector<bool> BasicBlockProfiler::GetCoverageBitmap(Isolate* isolate) {
170
  DisallowGarbageCollection no_gc;
171 172 173 174 175 176
  ArrayList list(isolate->heap()->basic_block_profiling_data());
  std::vector<bool> out;
  int list_length = list.Length();
  for (int i = 0; i < list_length; ++i) {
    BasicBlockProfilerData data(
        OnHeapBasicBlockProfilerData::cast(list.Get(i)));
Ng Zhi An's avatar
Ng Zhi An committed
177 178
    for (size_t j = 0; j < data.n_blocks(); ++j) {
      out.push_back(data.counts_[j] > 0);
179 180 181 182 183
    }
  }
  return out;
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197
void BasicBlockProfilerData::Log(Isolate* isolate) {
  bool any_nonzero_counter = false;
  for (size_t i = 0; i < n_blocks(); ++i) {
    if (counts_[i] > 0) {
      any_nonzero_counter = true;
      isolate->logger()->BasicBlockCounterEvent(function_name_.c_str(),
                                                block_ids_[i], counts_[i]);
    }
  }
  if (any_nonzero_counter) {
    isolate->logger()->BuiltinHashEvent(function_name_.c_str(), hash_);
  }
}

198
std::ostream& operator<<(std::ostream& os, const BasicBlockProfilerData& d) {
199 200 201 202 203
  if (std::all_of(d.counts_.cbegin(), d.counts_.cend(),
                  [](uint32_t count) { return count == 0; })) {
    // No data was collected for this function.
    return os;
  }
204 205 206 207 208
  const char* name = "unknown function";
  if (!d.function_name_.empty()) {
    name = d.function_name_.c_str();
  }
  if (!d.schedule_.empty()) {
209 210
    os << "schedule for " << name << " (B0 entered " << d.counts_[0]
       << " 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 216
  pairs.reserve(d.n_blocks());
  for (size_t i = 0; i < d.n_blocks(); ++i) {
217 218 219 220
    pairs.push_back(std::make_pair(i, d.counts_[i]));
  }
  std::sort(
      pairs.begin(), pairs.end(),
221
      [=](std::pair<size_t, uint32_t> left, std::pair<size_t, uint32_t> right) {
222 223 224
        if (right.second == left.second) return left.first < right.first;
        return right.second < left.second;
      });
225 226 227
  for (auto it : pairs) {
    if (it.second == 0) break;
    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 234 235 236 237
  }
  return os;
}

}  // namespace internal
}  // namespace v8