/* * Copyright (c) 2016, Google Inc. * All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "builder.h" #include <fcntl.h> #include <unistd.h> #include <map> #include <unordered_map> #include <unordered_set> #include <utility> #include <vector> #include <iostream> #include "google/protobuf/io/gzip_stream.h" #include "google/protobuf/io/zero_copy_stream_impl.h" using google::protobuf::io::StringOutputStream; using google::protobuf::io::GzipOutputStream; using google::protobuf::io::FileOutputStream; using google::protobuf::RepeatedField; namespace perftools { namespace profiles { Builder::Builder() : profile_(new Profile()) { // string_table[0] must be "" profile_->add_string_table(""); } int64 Builder::StringId(const char *str) { if (str == nullptr || !str[0]) { return 0; } const int64 index = profile_->string_table_size(); const auto inserted = strings_.emplace(str, index); if (!inserted.second) { // Failed to insert -- use existing id. return inserted.first->second; } profile_->add_string_table(inserted.first->first); return index; } uint64 Builder::FunctionId(const char *name, const char *system_name, const char *file, int64 start_line) { int64 name_index = StringId(name); int64 system_name_index = StringId(system_name); int64 file_index = StringId(file); Function fn(name_index, system_name_index, file_index, start_line); int64 index = profile_->function_size() + 1; const auto inserted = functions_.insert(std::make_pair(fn, index)); const bool insert_successful = inserted.second; if (!insert_successful) { const auto existing_function = inserted.first; return existing_function->second; } auto function = profile_->add_function(); function->set_id(index); function->set_name(name_index); function->set_system_name(system_name_index); function->set_filename(file_index); function->set_start_line(start_line); return index; } bool Builder::Emit(string *output) { *output = ""; if (!profile_ || !Finalize()) { return false; } return Marshal(*profile_, output); } bool Builder::Marshal(const Profile &profile, string *output) { *output = ""; StringOutputStream stream(output); GzipOutputStream gzip_stream(&stream); if (!profile.SerializeToZeroCopyStream(&gzip_stream)) { std::cerr << "Failed to serialize to gzip stream"; return false; } return gzip_stream.Close(); } bool Builder::MarshalToFile(const Profile &profile, int fd) { FileOutputStream stream(fd); GzipOutputStream gzip_stream(&stream); if (!profile.SerializeToZeroCopyStream(&gzip_stream)) { std::cerr << "Failed to serialize to gzip stream"; return false; } return gzip_stream.Close(); } bool Builder::MarshalToFile(const Profile &profile, const char *filename) { int fd = TEMP_FAILURE_RETRY(open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0444)); if (fd == -1) { return false; } int ret = MarshalToFile(profile, fd); close(fd); return ret; } // Returns a bool indicating if the profile is valid. It logs any // errors it encounters. bool Builder::CheckValid(const Profile &profile) { std::unordered_set<uint64> mapping_ids; for (const auto &mapping : profile.mapping()) { const int64 id = mapping.id(); if (id != 0) { const bool insert_successful = mapping_ids.insert(id).second; if (!insert_successful) { std::cerr << "Duplicate mapping id: " << id; return false; } } } std::unordered_set<uint64> function_ids; for (const auto &function : profile.function()) { const int64 id = function.id(); if (id != 0) { const bool insert_successful = function_ids.insert(id).second; if (!insert_successful) { std::cerr << "Duplicate function id: " << id; return false; } } } std::unordered_set<uint64> location_ids; for (const auto &location : profile.location()) { const int64 id = location.id(); if (id != 0) { const bool insert_successful = location_ids.insert(id).second; if (!insert_successful) { std::cerr << "Duplicate location id: " << id; return false; } } const int64 mapping_id = location.mapping_id(); if (mapping_id != 0 && mapping_ids.count(mapping_id) == 0) { std::cerr << "Missing mapping " << mapping_id << " from location " << id; return false; } for (const auto &line : location.line()) { int64 function_id = line.function_id(); if (function_id != 0 && function_ids.count(function_id) == 0) { std::cerr << "Missing function " << function_id; return false; } } } int sample_type_len = profile.sample_type_size(); if (sample_type_len == 0) { std::cerr << "No sample type specified"; return false; } for (const auto &sample : profile.sample()) { if (sample.value_size() != sample_type_len) { std::cerr << "Found sample with " << sample.value_size() << " values, expecting " << sample_type_len; return false; } for (uint64 location_id : sample.location_id()) { if (location_id == 0) { std::cerr << "Sample referencing location_id=0"; return false; } if (location_ids.count(location_id) == 0) { std::cerr << "Missing location " << location_id; return false; } } for (const auto &label : sample.label()) { int64 str = label.str(); int64 num = label.num(); if (str != 0 && num != 0) { std::cerr << "One of str/num must be unset, got " << str << "," << num; return false; } } } return true; } // Finalizes the profile for serialization. // - Creates missing locations for unsymbolized profiles. // - Associates locations to the corresponding mappings. bool Builder::Finalize() { if (profile_->location_size() == 0) { std::unordered_map<uint64, uint64> address_to_id; for (auto &sample : *profile_->mutable_sample()) { // Copy sample locations into a temp vector, and then clear and // repopulate it with the corresponding location IDs. const RepeatedField<uint64> addresses = sample.location_id(); sample.clear_location_id(); for (uint64 address : addresses) { int64 index = address_to_id.size() + 1; const auto inserted = address_to_id.emplace(address, index); if (inserted.second) { auto loc = profile_->add_location(); loc->set_id(index); loc->set_address(address); } sample.add_location_id(inserted.first->second); } } } // Look up location address on mapping ranges. if (profile_->mapping_size() > 0) { std::map<uint64, std::pair<uint64, uint64> > mapping_map; for (const auto &mapping : profile_->mapping()) { mapping_map[mapping.memory_start()] = std::make_pair(mapping.memory_limit(), mapping.id()); } for (auto &loc : *profile_->mutable_location()) { if (loc.address() != 0 && loc.mapping_id() == 0) { auto mapping = mapping_map.upper_bound(loc.address()); if (mapping == mapping_map.begin()) { // Address landed before the first mapping continue; } mapping--; uint64 limit = mapping->second.first; uint64 id = mapping->second.second; if (loc.address() <= limit) { loc.set_mapping_id(id); } } } } return CheckValid(*profile_); } } // namespace profiles } // namespace perftools