Home | History | Annotate | Download | only in src
      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