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