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