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
22using google::protobuf::io::StringOutputStream;
23using google::protobuf::io::GzipOutputStream;
24using google::protobuf::io::FileOutputStream;
25using google::protobuf::RepeatedField;
26
27namespace perftools {
28namespace profiles {
29
30Builder::Builder() : profile_(new Profile()) {
31  // string_table[0] must be ""
32  profile_->add_string_table("");
33}
34
35int64 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
50uint64 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
75bool Builder::Emit(string *output) {
76  *output = "";
77  if (!profile_ || !Finalize()) {
78    return false;
79  }
80  return Marshal(*profile_, output);
81}
82
83bool 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
94bool 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
104bool 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.
117bool 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.
205bool 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