Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: handler/mac/crash_report_upload_thread.cc

Issue 982613002: handler: Add report upload (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « handler/mac/crash_report_upload_thread.h ('k') | handler/mac/main.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Crashpad Authors. All rights reserved. 1 // Copyright 2015 The Crashpad Authors. All rights reserved.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "handler/mac/crash_report_upload_thread.h" 15 #include "handler/mac/crash_report_upload_thread.h"
16 16
17 #include <errno.h> 17 #include <errno.h>
18 18
19 #include <map>
19 #include <vector> 20 #include <vector>
21 #include <utility>
20 22
21 #include "base/logging.h" 23 #include "base/logging.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "snapshot/minidump/process_snapshot_minidump.h"
26 #include "snapshot/module_snapshot.h"
27 #include "util/file/file_reader.h"
28 #include "util/net/http_body.h"
29 #include "util/net/http_multipart_builder.h"
30 #include "util/net/http_transport.h"
22 31
23 namespace crashpad { 32 namespace crashpad {
24 33
25 CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database) 34 namespace {
26 : database_(database), 35
36 // Given a minidump file readable by |minidump_file_reader|, returns a map of
37 // key-value pairs to use as HTTP form parameters for upload to a Breakpad
38 // server. The map is built by combining the process simple annotations map with
39 // each module’s simple annotations map. In the case of duplicate keys, the map
40 // will retain the first value found for any key, and will log a warning about
41 // discarded values. Each module’s annotations vector is also examined and built
42 // into a single string value, with distinct elements separated by newlines, and
43 // stored at the key named “list_annotations”, which supersedes any other key
44 // found by that name.
45 //
46 // In the event of an error reading the minidump file, a message will be logged.
47 std::map<std::string, std::string> BreakpadHTTPFormParametersFromMinidump(
48 FileReader* minidump_file_reader) {
49 ProcessSnapshotMinidump minidump_process_snapshot;
50 if (!minidump_process_snapshot.Initialize(minidump_file_reader)) {
51 return std::map<std::string, std::string>();
52 }
53
54 std::map<std::string, std::string> parameters =
55 minidump_process_snapshot.AnnotationsSimpleMap();
56
57 std::string list_annotations;
58 for (const ModuleSnapshot* module : minidump_process_snapshot.Modules()) {
59 for (const auto& kv : module->AnnotationsSimpleMap()) {
60 if (parameters.find(kv.first) != parameters.end()) {
61 LOG(WARNING) << "duplicate key " << kv.first << ", discarding value "
62 << kv.second;
63 } else {
64 parameters.insert(kv);
65 }
66 }
67
68 for (std::string annotation : module->AnnotationsVector()) {
69 list_annotations.append(annotation);
70 list_annotations.append("\n");
71 }
72 }
73
74 if (!list_annotations.empty()) {
75 // Remove the final newline character.
76 list_annotations.pop_back();
77
78 const char kListAnnotationsKey[] = "list_annotations";
79 auto it = parameters.find(kListAnnotationsKey);
80 if (it != parameters.end()) {
81 LOG(WARNING) << "duplicate key " << kListAnnotationsKey
82 << ", discarding value " << it->second;
83 it->second = list_annotations;
84 } else {
85 parameters.insert(std::make_pair(kListAnnotationsKey, list_annotations));
86 }
87 }
88
89 return parameters;
90 }
91
92 // Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to
93 // false upon destruction unless disarmed by calling Fire() or Disarm(). Fire()
94 // triggers an immediate call. Armed upon construction.
95 class CallRecordUploadAttempt {
96 public:
97 CallRecordUploadAttempt(CrashReportDatabase* database,
98 const CrashReportDatabase::Report* report)
99 : database_(database),
100 report_(report) {
101 }
102
103 ~CallRecordUploadAttempt() {
104 Fire();
105 }
106
107 void Fire() {
108 if (report_) {
109 database_->RecordUploadAttempt(report_, false, std::string());
110 }
111
112 Disarm();
113 }
114
115 void Disarm() {
116 report_ = nullptr;
117 }
118
119 private:
120 CrashReportDatabase* database_; // weak
121 const CrashReportDatabase::Report* report_; // weak
122
123 DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt);
124 };
125
126 } // namespace
127
128 CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
129 const std::string& url)
130 : url_(url),
131 database_(database),
27 semaphore_(0), 132 semaphore_(0),
28 thread_(0), 133 thread_(0),
29 running_(false) { 134 running_(false) {
30 } 135 }
31 136
32 CrashReportUploadThread::~CrashReportUploadThread() { 137 CrashReportUploadThread::~CrashReportUploadThread() {
33 DCHECK(!running_); 138 DCHECK(!running_);
34 DCHECK(!thread_); 139 DCHECK(!thread_);
35 } 140 }
36 141
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 // Respect Stop() being called after at least one attempt to process a 202 // Respect Stop() being called after at least one attempt to process a
98 // report. 203 // report.
99 if (!running_) { 204 if (!running_) {
100 return; 205 return;
101 } 206 }
102 } 207 }
103 } 208 }
104 209
105 void CrashReportUploadThread::ProcessPendingReport( 210 void CrashReportUploadThread::ProcessPendingReport(
106 const CrashReportDatabase::Report& report) { 211 const CrashReportDatabase::Report& report) {
107 // TODO(mark): Actually upload the report, if uploads are enabled. 212 // TODO(mark): Allow uploads to be disabled.
108 database_->SkipReportUpload(report.uuid); 213 // TODO(mark): Rate-limit uploads.
214
215 const CrashReportDatabase::Report* upload_report;
216 CrashReportDatabase::OperationStatus status =
217 database_->GetReportForUploading(report.uuid, &upload_report);
218 switch (status) {
219 case CrashReportDatabase::kNoError:
220 break;
221
222 case CrashReportDatabase::kBusyError:
223 return;
224
225 case CrashReportDatabase::kReportNotFound:
226 case CrashReportDatabase::kFileSystemError:
227 case CrashReportDatabase::kDatabaseError:
228 // In these cases, SkipReportUpload() might not work either, but it’s best
229 // to at least try to get the report out of the way.
230 database_->SkipReportUpload(report.uuid);
231 return;
232 }
233
234 CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report);
235
236 std::string response_body;
237 UploadResult upload_result = UploadReport(upload_report, &response_body);
238 switch (upload_result) {
239 case UploadResult::kSuccess:
240 call_record_upload_attempt.Disarm();
241 database_->RecordUploadAttempt(upload_report, true, response_body);
242 break;
243 case UploadResult::kPermanentFailure:
244 case UploadResult::kRetry:
245 call_record_upload_attempt.Fire();
246
247 // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod()
248 // if the result was kRetry and the report hasn’t already been retried
249 // too many times.
250 database_->SkipReportUpload(report.uuid);
251 break;
252 }
253 }
254
255 CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
256 const CrashReportDatabase::Report* report,
257 std::string* response_body) {
258 std::map<std::string, std::string> parameters;
259
260 {
261 FileReader minidump_file_reader;
262 if (!minidump_file_reader.Open(report->file_path)) {
263 // If the minidump file can’t be opened, all hope is lost.
264 return UploadResult::kPermanentFailure;
265 }
266
267 // If the minidump file could be opened, ignore any errors that might occur
268 // when attempting to interpret it. This may result in its being uploaded
269 // with few or no parameters, but as long as there’s a dump file, the server
270 // can decide what to do with it.
271 parameters = BreakpadHTTPFormParametersFromMinidump(&minidump_file_reader);
272 }
273
274 HTTPMultipartBuilder http_multipart_builder;
275
276 const char kMinidumpKey[] = "upload_file_minidump";
277
278 for (const auto& kv : parameters) {
279 if (kv.first == kMinidumpKey) {
280 LOG(WARNING) << "reserved key " << kv.first << ", discarding value "
281 << kv.second;
282 } else {
283 http_multipart_builder.SetFormData(kv.first, kv.second);
284 }
285 }
286
287 http_multipart_builder.SetFileAttachment(kMinidumpKey,
288 report->file_path.BaseName().value(),
289 report->file_path,
290 "application/octet-stream");
291
292 // TODO(mark): There should be a timeout option for upload.
293 scoped_ptr<HTTPTransport> http_transport(HTTPTransport::Create());
294 http_transport->SetURL(url_);
295 HTTPHeaders::value_type content_type =
296 http_multipart_builder.GetContentType();
297 http_transport->SetHeader(content_type.first, content_type.second);
298 http_transport->SetBodyStream(http_multipart_builder.GetBodyStream().Pass());
299
300 if (!http_transport->ExecuteSynchronously(response_body)) {
301 return UploadResult::kRetry;
302 }
303
304 return UploadResult::kSuccess;
109 } 305 }
110 306
111 // static 307 // static
112 void* CrashReportUploadThread::RunThreadMain(void* arg) { 308 void* CrashReportUploadThread::RunThreadMain(void* arg) {
113 CrashReportUploadThread* self = static_cast<CrashReportUploadThread*>(arg); 309 CrashReportUploadThread* self = static_cast<CrashReportUploadThread*>(arg);
114 self->ThreadMain(); 310 self->ThreadMain();
115 return nullptr; 311 return nullptr;
116 } 312 }
117 313
118 } // namespace crashpad 314 } // namespace crashpad
OLDNEW
« no previous file with comments | « handler/mac/crash_report_upload_thread.h ('k') | handler/mac/main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698