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 
Builder()30 Builder::Builder() : profile_(new Profile()) {
31   // string_table[0] must be ""
32   profile_->add_string_table("");
33 }
34 
StringId(const char * str)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 
FunctionId(const char * name,const char * system_name,const char * file,int64 start_line)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 
Emit(string * output)75 bool Builder::Emit(string *output) {
76   *output = "";
77   if (!profile_ || !Finalize()) {
78     return false;
79   }
80   return Marshal(*profile_, output);
81 }
82 
Marshal(const Profile & profile,string * output)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 
MarshalToFile(const Profile & profile,int fd)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 
MarshalToFile(const Profile & profile,const char * filename)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.
CheckValid(const Profile & profile)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.
Finalize()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