1// Copyright 2020 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/builtins/profile-data-reader.h"
6
7#include <fstream>
8#include <iostream>
9#include <unordered_map>
10
11#include "src/base/lazy-instance.h"
12#include "src/flags/flags.h"
13#include "src/utils/utils.h"
14
15namespace v8 {
16namespace internal {
17
18namespace {
19
20class ProfileDataFromFileInternal : public ProfileDataFromFile {
21 public:
22  bool hash_has_value() const { return hash_has_value_; }
23
24  void set_hash(int hash) {
25    hash_ = hash;
26    hash_has_value_ = true;
27  }
28
29  void AddCountToBlock(size_t block_id, double count) {
30    if (block_counts_by_id_.size() <= block_id) {
31      // std::vector initializes new data to zero when resizing.
32      block_counts_by_id_.resize(block_id + 1);
33    }
34    block_counts_by_id_[block_id] += count;
35  }
36
37 private:
38  bool hash_has_value_ = false;
39};
40
41const std::unordered_map<std::string, ProfileDataFromFileInternal>&
42EnsureInitProfileData() {
43  static base::LeakyObject<
44      std::unordered_map<std::string, ProfileDataFromFileInternal>>
45      data;
46  static bool initialized = false;
47
48  if (initialized) return *data.get();
49  initialized = true;
50  const char* filename = FLAG_turbo_profiling_log_file;
51  if (filename == nullptr) return *data.get();
52  std::ifstream file(filename);
53  CHECK_WITH_MSG(file.good(), "Can't read log file");
54  for (std::string line; std::getline(file, line);) {
55    std::string token;
56    std::istringstream line_stream(line);
57    if (!std::getline(line_stream, token, ',')) continue;
58    if (token == ProfileDataFromFileConstants::kBlockCounterMarker) {
59      // Any line starting with kBlockCounterMarker is a block usage count.
60      // As defined by Logger::BasicBlockCounterEvent, the format is:
61      //   literal kBlockCounterMarker , builtin_name , block_id , usage_count
62      std::string builtin_name;
63      CHECK(std::getline(line_stream, builtin_name, ','));
64      CHECK(std::getline(line_stream, token, ','));
65      char* end = nullptr;
66      uint32_t id = static_cast<uint32_t>(strtoul(token.c_str(), &end, 0));
67      CHECK(errno == 0 && end != token.c_str());
68      std::getline(line_stream, token, ',');
69      CHECK(line_stream.eof());
70      double count = strtod(token.c_str(), &end);
71      CHECK(errno == 0 && end != token.c_str());
72      ProfileDataFromFileInternal& counters_and_hash =
73          (*data.get())[builtin_name];
74      // We allow concatenating data from several Isolates, so we might see the
75      // same block multiple times. Just sum them all.
76      counters_and_hash.AddCountToBlock(id, count);
77    } else if (token == ProfileDataFromFileConstants::kBuiltinHashMarker) {
78      // Any line starting with kBuiltinHashMarker is a function hash record.
79      // As defined by Logger::BuiltinHashEvent, the format is:
80      //   literal kBuiltinHashMarker , builtin_name , hash
81      std::string builtin_name;
82      CHECK(std::getline(line_stream, builtin_name, ','));
83      std::getline(line_stream, token, ',');
84      CHECK(line_stream.eof());
85      char* end = nullptr;
86      int hash = static_cast<int>(strtol(token.c_str(), &end, 0));
87      CHECK(errno == 0 && end != token.c_str());
88      ProfileDataFromFileInternal& counters_and_hash =
89          (*data.get())[builtin_name];
90      // We allow concatenating data from several Isolates, but expect them all
91      // to be running the same build. Any file with mismatched hashes for a
92      // function is considered ill-formed.
93      CHECK_IMPLIES(counters_and_hash.hash_has_value(),
94                    counters_and_hash.hash() == hash);
95      counters_and_hash.set_hash(hash);
96    }
97  }
98  for (const auto& pair : *data.get()) {
99    // Every function is required to have a hash in the log.
100    CHECK(pair.second.hash_has_value());
101  }
102  if (data.get()->size() == 0) {
103    PrintF(
104        "No basic block counters were found in log file.\n"
105        "Did you build with v8_enable_builtins_profiling=true\n"
106        "and run with --turbo-profiling-log-builtins?\n");
107  }
108
109  return *data.get();
110}
111
112}  // namespace
113
114const ProfileDataFromFile* ProfileDataFromFile::TryRead(const char* name) {
115  const auto& data = EnsureInitProfileData();
116  auto it = data.find(name);
117  return it == data.end() ? nullptr : &it->second;
118}
119
120}  // namespace internal
121}  // namespace v8
122