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