1 /* 2 * Copyright (c) 2016, Google Inc. 3 * All rights reserved. 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "builder.h" 9 10 #include <fcntl.h> 11 #include <unistd.h> 12 13 #include <map> 14 #include <unordered_map> 15 #include <unordered_set> 16 #include <utility> 17 #include <vector> 18 #include <iostream> 19 #include "google/protobuf/io/gzip_stream.h" 20 #include "google/protobuf/io/zero_copy_stream_impl.h" 21 22 using google::protobuf::io::StringOutputStream; 23 using google::protobuf::io::GzipOutputStream; 24 using google::protobuf::io::FileOutputStream; 25 using google::protobuf::RepeatedField; 26 27 namespace perftools { 28 namespace profiles { 29 30 Builder::Builder() : profile_(new Profile()) { 31 // string_table[0] must be "" 32 profile_->add_string_table(""); 33 } 34 35 int64 Builder::StringId(const char *str) { 36 if (str == nullptr || !str[0]) { 37 return 0; 38 } 39 40 const int64 index = profile_->string_table_size(); 41 const auto inserted = strings_.emplace(str, index); 42 if (!inserted.second) { 43 // Failed to insert -- use existing id. 44 return inserted.first->second; 45 } 46 profile_->add_string_table(inserted.first->first); 47 return index; 48 } 49 50 uint64 Builder::FunctionId(const char *name, const char *system_name, 51 const char *file, int64 start_line) { 52 int64 name_index = StringId(name); 53 int64 system_name_index = StringId(system_name); 54 int64 file_index = StringId(file); 55 56 Function fn(name_index, system_name_index, file_index, start_line); 57 58 int64 index = profile_->function_size() + 1; 59 const auto inserted = functions_.insert(std::make_pair(fn, index)); 60 const bool insert_successful = inserted.second; 61 if (!insert_successful) { 62 const auto existing_function = inserted.first; 63 return existing_function->second; 64 } 65 66 auto function = profile_->add_function(); 67 function->set_id(index); 68 function->set_name(name_index); 69 function->set_system_name(system_name_index); 70 function->set_filename(file_index); 71 function->set_start_line(start_line); 72 return index; 73 } 74 75 bool Builder::Emit(string *output) { 76 *output = ""; 77 if (!profile_ || !Finalize()) { 78 return false; 79 } 80 return Marshal(*profile_, output); 81 } 82 83 bool Builder::Marshal(const Profile &profile, string *output) { 84 *output = ""; 85 StringOutputStream stream(output); 86 GzipOutputStream gzip_stream(&stream); 87 if (!profile.SerializeToZeroCopyStream(&gzip_stream)) { 88 std::cerr << "Failed to serialize to gzip stream"; 89 return false; 90 } 91 return gzip_stream.Close(); 92 } 93 94 bool Builder::MarshalToFile(const Profile &profile, int fd) { 95 FileOutputStream stream(fd); 96 GzipOutputStream gzip_stream(&stream); 97 if (!profile.SerializeToZeroCopyStream(&gzip_stream)) { 98 std::cerr << "Failed to serialize to gzip stream"; 99 return false; 100 } 101 return gzip_stream.Close(); 102 } 103 104 bool Builder::MarshalToFile(const Profile &profile, const char *filename) { 105 int fd = 106 TEMP_FAILURE_RETRY(open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0444)); 107 if (fd == -1) { 108 return false; 109 } 110 int ret = MarshalToFile(profile, fd); 111 close(fd); 112 return ret; 113 } 114 115 // Returns a bool indicating if the profile is valid. It logs any 116 // errors it encounters. 117 bool Builder::CheckValid(const Profile &profile) { 118 std::unordered_set<uint64> mapping_ids; 119 for (const auto &mapping : profile.mapping()) { 120 const int64 id = mapping.id(); 121 if (id != 0) { 122 const bool insert_successful = mapping_ids.insert(id).second; 123 if (!insert_successful) { 124 std::cerr << "Duplicate mapping id: " << id; 125 return false; 126 } 127 } 128 } 129 130 std::unordered_set<uint64> function_ids; 131 for (const auto &function : profile.function()) { 132 const int64 id = function.id(); 133 if (id != 0) { 134 const bool insert_successful = function_ids.insert(id).second; 135 if (!insert_successful) { 136 std::cerr << "Duplicate function id: " << id; 137 return false; 138 } 139 } 140 } 141 142 std::unordered_set<uint64> location_ids; 143 for (const auto &location : profile.location()) { 144 const int64 id = location.id(); 145 if (id != 0) { 146 const bool insert_successful = location_ids.insert(id).second; 147 if (!insert_successful) { 148 std::cerr << "Duplicate location id: " << id; 149 return false; 150 } 151 } 152 const int64 mapping_id = location.mapping_id(); 153 if (mapping_id != 0 && mapping_ids.count(mapping_id) == 0) { 154 std::cerr << "Missing mapping " << mapping_id << " from location " << id; 155 return false; 156 } 157 for (const auto &line : location.line()) { 158 int64 function_id = line.function_id(); 159 if (function_id != 0 && function_ids.count(function_id) == 0) { 160 std::cerr << "Missing function " << function_id; 161 return false; 162 } 163 } 164 } 165 166 int sample_type_len = profile.sample_type_size(); 167 if (sample_type_len == 0) { 168 std::cerr << "No sample type specified"; 169 return false; 170 } 171 172 for (const auto &sample : profile.sample()) { 173 if (sample.value_size() != sample_type_len) { 174 std::cerr << "Found sample with " << sample.value_size() 175 << " values, expecting " << sample_type_len; 176 return false; 177 } 178 for (uint64 location_id : sample.location_id()) { 179 if (location_id == 0) { 180 std::cerr << "Sample referencing location_id=0"; 181 return false; 182 } 183 184 if (location_ids.count(location_id) == 0) { 185 std::cerr << "Missing location " << location_id; 186 return false; 187 } 188 } 189 190 for (const auto &label : sample.label()) { 191 int64 str = label.str(); 192 int64 num = label.num(); 193 if (str != 0 && num != 0) { 194 std::cerr << "One of str/num must be unset, got " << str << "," << num; 195 return false; 196 } 197 } 198 } 199 return true; 200 } 201 202 // Finalizes the profile for serialization. 203 // - Creates missing locations for unsymbolized profiles. 204 // - Associates locations to the corresponding mappings. 205 bool Builder::Finalize() { 206 if (profile_->location_size() == 0) { 207 std::unordered_map<uint64, uint64> address_to_id; 208 for (auto &sample : *profile_->mutable_sample()) { 209 // Copy sample locations into a temp vector, and then clear and 210 // repopulate it with the corresponding location IDs. 211 const RepeatedField<uint64> addresses = sample.location_id(); 212 sample.clear_location_id(); 213 for (uint64 address : addresses) { 214 int64 index = address_to_id.size() + 1; 215 const auto inserted = address_to_id.emplace(address, index); 216 if (inserted.second) { 217 auto loc = profile_->add_location(); 218 loc->set_id(index); 219 loc->set_address(address); 220 } 221 sample.add_location_id(inserted.first->second); 222 } 223 } 224 } 225 226 // Look up location address on mapping ranges. 227 if (profile_->mapping_size() > 0) { 228 std::map<uint64, std::pair<uint64, uint64> > mapping_map; 229 for (const auto &mapping : profile_->mapping()) { 230 mapping_map[mapping.memory_start()] = 231 std::make_pair(mapping.memory_limit(), mapping.id()); 232 } 233 234 for (auto &loc : *profile_->mutable_location()) { 235 if (loc.address() != 0 && loc.mapping_id() == 0) { 236 auto mapping = mapping_map.upper_bound(loc.address()); 237 if (mapping == mapping_map.begin()) { 238 // Address landed before the first mapping 239 continue; 240 } 241 mapping--; 242 uint64 limit = mapping->second.first; 243 uint64 id = mapping->second.second; 244 245 if (loc.address() <= limit) { 246 loc.set_mapping_id(id); 247 } 248 } 249 } 250 } 251 return CheckValid(*profile_); 252 } 253 254 } // namespace profiles 255 } // namespace perftools 256