1 | // Copyright (c) 2011 Google Inc. |
2 | // All rights reserved. |
3 | // |
4 | // Redistribution and use in source and binary forms, with or without |
5 | // modification, are permitted provided that the following conditions are |
6 | // met: |
7 | // |
8 | // * Redistributions of source code must retain the above copyright |
9 | // notice, this list of conditions and the following disclaimer. |
10 | // * Redistributions in binary form must reproduce the above |
11 | // copyright notice, this list of conditions and the following disclaimer |
12 | // in the documentation and/or other materials provided with the |
13 | // distribution. |
14 | // * Neither the name of Google Inc. nor the names of its |
15 | // contributors may be used to endorse or promote products derived from |
16 | // this software without specific prior written permission. |
17 | // |
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | |
30 | // symbol_upload.cc: implemented google_breakpad::sym_upload::Start, a helper |
31 | // function for linux symbol upload tool. |
32 | |
33 | #include "common/linux/symbol_upload.h" |
34 | |
35 | #include <assert.h> |
36 | #include <stdio.h> |
37 | |
38 | #include <functional> |
39 | #include <iostream> |
40 | #include <vector> |
41 | |
42 | #include "common/linux/http_upload.h" |
43 | #include "common/linux/libcurl_wrapper.h" |
44 | #include "common/linux/symbol_collector_client.h" |
45 | |
46 | namespace google_breakpad { |
47 | namespace sym_upload { |
48 | |
49 | void TokenizeByChar(const string& source_string, int c, |
50 | std::vector<string>* results) { |
51 | assert(results); |
52 | string::size_type cur_pos = 0, next_pos = 0; |
53 | while ((next_pos = source_string.find(c, cur_pos)) != string::npos) { |
54 | if (next_pos != cur_pos) |
55 | results->push_back(source_string.substr(cur_pos, next_pos - cur_pos)); |
56 | cur_pos = next_pos + 1; |
57 | } |
58 | if (cur_pos < source_string.size() && next_pos != cur_pos) |
59 | results->push_back(source_string.substr(cur_pos)); |
60 | } |
61 | |
62 | //============================================================================= |
63 | // Parse out the module line which have 5 parts. |
64 | // MODULE <os> <cpu> <uuid> <module-name> |
65 | bool ModuleDataForSymbolFile(const string& file, |
66 | std::vector<string>* module_parts) { |
67 | assert(module_parts); |
68 | const size_t kModulePartNumber = 5; |
69 | FILE* fp = fopen(file.c_str(), "r" ); |
70 | if (fp) { |
71 | char buffer[1024]; |
72 | if (fgets(buffer, sizeof(buffer), fp)) { |
73 | string line(buffer); |
74 | string::size_type line_break_pos = line.find_first_of('\n'); |
75 | if (line_break_pos == string::npos) { |
76 | assert(0 && "The file is invalid!" ); |
77 | fclose(fp); |
78 | return false; |
79 | } |
80 | line.resize(line_break_pos); |
81 | const char kDelimiter = ' '; |
82 | TokenizeByChar(line, kDelimiter, module_parts); |
83 | if (module_parts->size() != kModulePartNumber) |
84 | module_parts->clear(); |
85 | } |
86 | fclose(fp); |
87 | } |
88 | |
89 | return module_parts->size() == kModulePartNumber; |
90 | } |
91 | |
92 | //============================================================================= |
93 | string CompactIdentifier(const string& uuid) { |
94 | std::vector<string> components; |
95 | TokenizeByChar(uuid, '-', &components); |
96 | string result; |
97 | for (size_t i = 0; i < components.size(); ++i) |
98 | result += components[i]; |
99 | return result; |
100 | } |
101 | |
102 | // |options| describes the current sym_upload options. |
103 | // |module_parts| contains the strings parsed from the MODULE entry of the |
104 | // Breakpad symbol file being uploaded. |
105 | // |compacted_id| is the debug_id from the MODULE entry of the Breakpad symbol |
106 | // file being uploaded, with all hyphens removed. |
107 | bool SymUploadV1Start( |
108 | const Options& options, |
109 | std::vector<string> module_parts, |
110 | const string& compacted_id) { |
111 | std::map<string, string> parameters; |
112 | // Add parameters |
113 | if (!options.version.empty()) |
114 | parameters["version" ] = options.version; |
115 | |
116 | // MODULE <os> <cpu> <uuid> <module-name> |
117 | // 0 1 2 3 4 |
118 | parameters["os" ] = module_parts[1]; |
119 | parameters["cpu" ] = module_parts[2]; |
120 | parameters["debug_file" ] = module_parts[4]; |
121 | parameters["code_file" ] = module_parts[4]; |
122 | parameters["debug_identifier" ] = compacted_id; |
123 | |
124 | std::map<string, string> files; |
125 | files["symbol_file" ] = options.symbolsPath; |
126 | |
127 | string response, error; |
128 | long response_code; |
129 | bool success = HTTPUpload::SendRequest(options.uploadURLStr, |
130 | parameters, |
131 | files, |
132 | options.proxy, |
133 | options.proxy_user_pwd, |
134 | /*ca_certificate_file=*/"" , |
135 | &response, |
136 | &response_code, |
137 | &error); |
138 | |
139 | if (!success) { |
140 | printf("Failed to send symbol file: %s\n" , error.c_str()); |
141 | printf("Response code: %ld\n" , response_code); |
142 | printf("Response:\n" ); |
143 | printf("%s\n" , response.c_str()); |
144 | } else if (response_code == 0) { |
145 | printf("Failed to send symbol file: No response code\n" ); |
146 | } else if (response_code != 200) { |
147 | printf("Failed to send symbol file: Response code %ld\n" , response_code); |
148 | printf("Response:\n" ); |
149 | printf("%s\n" , response.c_str()); |
150 | } else { |
151 | printf("Successfully sent the symbol file.\n" ); |
152 | } |
153 | |
154 | return success; |
155 | } |
156 | |
157 | // |options| describes the current sym_upload options. |
158 | // |code_id| is the basename of the module for which symbols are being |
159 | // uploaded. |
160 | // |debug_id| is the debug_id of the module for which symbols are being |
161 | // uploaded. |
162 | bool SymUploadV2Start( |
163 | const Options& options, |
164 | const string& code_file, |
165 | const string& debug_id, |
166 | const string& type) { |
167 | google_breakpad::LibcurlWrapper libcurl_wrapper; |
168 | if (!libcurl_wrapper.Init()) { |
169 | printf("Failed to init google_breakpad::LibcurlWrapper.\n" ); |
170 | return false; |
171 | } |
172 | |
173 | if (!options.force) { |
174 | SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus( |
175 | &libcurl_wrapper, |
176 | options.uploadURLStr, |
177 | options.api_key, |
178 | code_file, |
179 | debug_id); |
180 | if (symbolStatus == SymbolStatus::Found) { |
181 | printf("Symbol file already exists, upload aborted." |
182 | " Use \"-f\" to overwrite.\n" ); |
183 | return true; |
184 | } else if (symbolStatus == SymbolStatus::Unknown) { |
185 | printf("Failed to check for existing symbol.\n" ); |
186 | return false; |
187 | } |
188 | } |
189 | |
190 | UploadUrlResponse uploadUrlResponse; |
191 | if (!SymbolCollectorClient::CreateUploadUrl( |
192 | &libcurl_wrapper, |
193 | options.uploadURLStr, |
194 | options.api_key, |
195 | &uploadUrlResponse)) { |
196 | printf("Failed to create upload URL.\n" ); |
197 | return false; |
198 | } |
199 | |
200 | string signed_url = uploadUrlResponse.upload_url; |
201 | string upload_key = uploadUrlResponse.upload_key; |
202 | string ; |
203 | string response; |
204 | long response_code; |
205 | |
206 | if (!libcurl_wrapper.SendPutRequest(signed_url, |
207 | options.symbolsPath, |
208 | &response_code, |
209 | &header, |
210 | &response)) { |
211 | printf("Failed to send symbol file.\n" ); |
212 | printf("Response code: %ld\n" , response_code); |
213 | printf("Response:\n" ); |
214 | printf("%s\n" , response.c_str()); |
215 | return false; |
216 | } else if (response_code == 0) { |
217 | printf("Failed to send symbol file: No response code\n" ); |
218 | return false; |
219 | } else if (response_code != 200) { |
220 | printf("Failed to send symbol file: Response code %ld\n" , response_code); |
221 | printf("Response:\n" ); |
222 | printf("%s\n" , response.c_str()); |
223 | return false; |
224 | } |
225 | |
226 | CompleteUploadResult completeUploadResult = |
227 | SymbolCollectorClient::CompleteUpload(&libcurl_wrapper, |
228 | options.uploadURLStr, |
229 | options.api_key, |
230 | upload_key, |
231 | code_file, |
232 | debug_id, |
233 | type); |
234 | if (completeUploadResult == CompleteUploadResult::Error) { |
235 | printf("Failed to complete upload.\n" ); |
236 | return false; |
237 | } else if (completeUploadResult == CompleteUploadResult::DuplicateData) { |
238 | printf("Uploaded file checksum matched existing file checksum," |
239 | " no change necessary.\n" ); |
240 | } else { |
241 | printf("Successfully sent the symbol file.\n" ); |
242 | } |
243 | |
244 | return true; |
245 | } |
246 | |
247 | //============================================================================= |
248 | void Start(Options* options) { |
249 | if (options->upload_protocol == UploadProtocol::SYM_UPLOAD_V2) { |
250 | string code_file; |
251 | string debug_id; |
252 | string type; |
253 | |
254 | if (options->type.empty() || options->type == kBreakpadSymbolType) { |
255 | // Breakpad upload so read these from input file. |
256 | std::vector<string> module_parts; |
257 | if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { |
258 | fprintf(stderr, "Failed to parse symbol file!\n" ); |
259 | return; |
260 | } |
261 | code_file = module_parts[4]; |
262 | debug_id = CompactIdentifier(module_parts[3]); |
263 | type = kBreakpadSymbolType; |
264 | } else { |
265 | // Native upload so these must be explicitly set. |
266 | code_file = options->code_file; |
267 | debug_id = options->debug_id; |
268 | type = options->type; |
269 | } |
270 | |
271 | options->success = SymUploadV2Start(*options, code_file, debug_id, type); |
272 | } else { |
273 | std::vector<string> module_parts; |
274 | if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { |
275 | fprintf(stderr, "Failed to parse symbol file!\n" ); |
276 | return; |
277 | } |
278 | const string compacted_id = CompactIdentifier(module_parts[3]); |
279 | options->success = SymUploadV1Start(*options, module_parts, compacted_id); |
280 | } |
281 | } |
282 | |
283 | } // namespace sym_upload |
284 | } // namespace google_breakpad |
285 | |