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
15 namespace v8 {
16 namespace internal {
17
18 namespace {
19
20 class ProfileDataFromFileInternal : public ProfileDataFromFile {
21 public:
hash_has_value() const22 bool hash_has_value() const { return hash_has_value_; }
23
set_hash(int hash)24 void set_hash(int hash) {
25 hash_ = hash;
26 hash_has_value_ = true;
27 }
28
AddCountToBlock(size_t block_id,double count)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
41 const std::unordered_map<std::string, ProfileDataFromFileInternal>&
EnsureInitProfileData()42 EnsureInitProfileData() {
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
TryRead(const char * name)114 const 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