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

Side by Side Diff: components/browser_watcher/postmortem_minidump_writer_win.cc

Issue 2685053003: Switch stability reports to use the crashed version's details (Closed)
Patch Set: Address bcwhite's comments Created 3 years, 10 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // Note: aside from using windows headers to obtain the definitions of minidump 5 // Note: aside from using windows headers to obtain the definitions of minidump
6 // structures, nothing here is windows specific. This seems like the best 6 // structures, nothing here is windows specific. This seems like the best
7 // approach given this code is for temporary experimentation on Windows. 7 // approach given this code is for temporary experimentation on Windows.
8 // Longer term, Crashpad will take over the minidump writing in this case as 8 // Longer term, Crashpad will take over the minidump writing in this case as
9 // well. 9 // well.
10 10
11 #include "components/browser_watcher/postmortem_minidump_writer.h" 11 #include "components/browser_watcher/postmortem_minidump_writer.h"
12 12
13 #include <windows.h> // NOLINT 13 #include <windows.h> // NOLINT
14 #include <dbghelp.h> 14 #include <dbghelp.h>
15 15
16 #include <map> 16 #include <map>
17 #include <type_traits> 17 #include <type_traits>
18 #include <vector> 18 #include <vector>
19 19
20 #include "base/files/file_util.h" 20 #include "base/files/file_util.h"
21 #include "base/macros.h" 21 #include "base/macros.h"
22 #include "base/metrics/histogram_macros.h"
22 #include "base/numerics/safe_math.h" 23 #include "base/numerics/safe_math.h"
23 #include "base/strings/string_piece.h" 24 #include "base/strings/string_piece.h"
25 #include "components/browser_watcher/stability_data_names.h"
24 #include "third_party/crashpad/crashpad/minidump/minidump_extensions.h" 26 #include "third_party/crashpad/crashpad/minidump/minidump_extensions.h"
25 27
26 namespace browser_watcher { 28 namespace browser_watcher {
27 29
28 namespace { 30 namespace {
29 31
30 // The stream type assigned to the minidump stream that holds the serialized 32 // The stream type assigned to the minidump stream that holds the serialized
31 // stability report. 33 // stability report.
32 // Note: the value was obtained by adding 1 to the stream type used for holding 34 // Note: the value was obtained by adding 1 to the stream type used for holding
33 // the SyzyAsan proto. 35 // the SyzyAsan proto.
34 // TODO(manzagop): centralize the stream type definitions to avoid issues. 36 // TODO(manzagop): centralize the stream type definitions to avoid issues.
35 const uint32_t kStabilityReportStreamType = 0x4B6B0002; 37 const uint32_t kStabilityReportStreamType = 0x4B6B0002;
36 38
39 struct ProductDetails {
40 std::string product;
41 std::string channel;
42 std::string platform;
43 std::string version;
44 };
45
46 bool GetStringFromTypedValueMap(
47 const google::protobuf::Map<std::string, TypedValue>& map,
48 const std::string& key,
49 std::string* out) {
50 DCHECK(out);
51
52 auto it = map.find(key);
53 if (it == map.end())
54 return false;
55
56 const TypedValue& value = it->second;
57 if (value.value_case() != TypedValue::kStringValue)
58 return false;
59
60 *out = value.string_value();
61 return true;
62 }
63
64 bool GetProductDetails(
65 const google::protobuf::Map<std::string, TypedValue>& global_data,
66 ProductDetails* product_details) {
67 DCHECK(product_details);
68
69 if (!GetStringFromTypedValueMap(global_data, kStabilityProduct,
70 &(product_details->product)))
71 return false;
72 if (!GetStringFromTypedValueMap(global_data, kStabilityChannel,
73 &(product_details->channel)))
74 return false;
75 if (!GetStringFromTypedValueMap(global_data, kStabilityPlatform,
76 &(product_details->platform)))
77 return false;
78 return GetStringFromTypedValueMap(global_data, kStabilityVersion,
79 &(product_details->version));
80 }
81
37 int64_t GetFileOffset(base::File* file) { 82 int64_t GetFileOffset(base::File* file) {
38 DCHECK(file); 83 DCHECK(file);
39 return file->Seek(base::File::FROM_CURRENT, 0LL); 84 return file->Seek(base::File::FROM_CURRENT, 0LL);
40 } 85 }
41 86
42 // Returns true if the file is empty, and false if the file is not empty or if 87 // Returns true if the file is empty, and false if the file is not empty or if
43 // there is an error. 88 // there is an error.
44 bool IsFileEmpty(base::File* file) { 89 bool IsFileEmpty(base::File* file) {
45 DCHECK(file); 90 DCHECK(file);
46 int64_t end = file->Seek(base::File::FROM_END, 0LL); 91 int64_t end = file->Seek(base::File::FROM_END, 0LL);
47 return end == 0LL; 92 return end == 0LL;
48 } 93 }
49 94
50 // A class with functionality for writing minimal minidump containers to wrap 95 // A class with functionality for writing minimal minidump containers to wrap
51 // postmortem stability reports. 96 // postmortem stability reports.
52 // TODO(manzagop): remove this class once Crashpad takes over writing postmortem 97 // TODO(manzagop): remove this class once Crashpad takes over writing postmortem
53 // minidumps. 98 // minidumps.
54 // TODO(manzagop): revisit where the module information should be transported, 99 // TODO(manzagop): revisit where the module information should be transported,
55 // in the protocol buffer or in a module stream. 100 // in the protocol buffer or in a module stream.
56 class PostmortemMinidumpWriter { 101 class PostmortemMinidumpWriter {
57 public: 102 public:
103 // DO NOT CHANGE VALUES. This is logged persistently in a histogram.
104 enum WriteStatus {
105 NONE = 0,
bcwhite 2017/02/13 18:35:07 Omit NONE if you never send it.
manzagop (departed) 2017/02/13 19:22:36 Done.
106 SUCCESS = 1,
107 FAILED = 2,
108 FAILED_MISSING_PRODUCT_DETAILS = 3,
109 WRITE_STATUS_MAX = 4
110 };
111
58 PostmortemMinidumpWriter(); 112 PostmortemMinidumpWriter();
59 ~PostmortemMinidumpWriter(); 113 ~PostmortemMinidumpWriter();
60 114
61 // Write to |minidump_file| a minimal minidump that wraps |report|. Returns 115 // Write to |minidump_file| a minimal minidump that wraps |report|. Returns
62 // true on success, false otherwise. 116 // true on success, false otherwise.
63 // Note: the caller owns |minidump_file| and is responsible for keeping it 117 // Note: the caller owns |minidump_file| and is responsible for keeping it
64 // valid for this object's lifetime. |minidump_file| is expected to be empty 118 // valid for this object's lifetime. |minidump_file| is expected to be empty
65 // and a binary stream. 119 // and a binary stream.
66 bool WriteDump(base::PlatformFile minidump_file, 120 bool WriteDump(base::PlatformFile minidump_file,
67 const StabilityReport& report, 121 const crashpad::UUID& client_id,
68 const MinidumpInfo& minidump_info); 122 const crashpad::UUID& report_id,
123 StabilityReport* report);
69 124
70 private: 125 private:
71 // An offset within a minidump file. Note: using this type to avoid including 126 // An offset within a minidump file. Note: using this type to avoid including
72 // windows.h and relying on the RVA type. 127 // windows.h and relying on the RVA type.
73 using FilePosition = uint32_t; 128 using FilePosition = uint32_t;
74 129
75 // The minidump header is always located at the head. 130 // The minidump header is always located at the head.
76 static const FilePosition kHeaderPos = 0U; 131 static const FilePosition kHeaderPos = 0U;
77 132
78 bool WriteDumpImpl(const StabilityReport& report, 133 bool WriteDumpImpl(const StabilityReport& report,
79 const MinidumpInfo& minidump_info); 134 const crashpad::UUID& client_id,
135 const crashpad::UUID& report_id,
136 const ProductDetails& product_details);
80 137
81 bool AppendCrashpadInfo(const crashpad::UUID& client_id, 138 bool AppendCrashpadInfo(const crashpad::UUID& client_id,
82 const crashpad::UUID& report_id, 139 const crashpad::UUID& report_id,
83 const std::map<std::string, std::string>& crash_keys); 140 const std::map<std::string, std::string>& crash_keys);
84 141
85 bool AppendCrashpadDictionaryEntry( 142 bool AppendCrashpadDictionaryEntry(
86 const std::string& key, 143 const std::string& key,
87 const std::string& value, 144 const std::string& value,
88 std::vector<crashpad::MinidumpSimpleStringDictionaryEntry>* entries); 145 std::vector<crashpad::MinidumpSimpleStringDictionaryEntry>* entries);
89 146
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 187
131 PostmortemMinidumpWriter::PostmortemMinidumpWriter() 188 PostmortemMinidumpWriter::PostmortemMinidumpWriter()
132 : next_available_byte_(0U), minidump_file_(nullptr) {} 189 : next_available_byte_(0U), minidump_file_(nullptr) {}
133 190
134 PostmortemMinidumpWriter::~PostmortemMinidumpWriter() { 191 PostmortemMinidumpWriter::~PostmortemMinidumpWriter() {
135 DCHECK_EQ(nullptr, minidump_file_); 192 DCHECK_EQ(nullptr, minidump_file_);
136 } 193 }
137 194
138 bool PostmortemMinidumpWriter::WriteDump( 195 bool PostmortemMinidumpWriter::WriteDump(
139 base::PlatformFile minidump_platform_file, 196 base::PlatformFile minidump_platform_file,
140 const StabilityReport& report, 197 const crashpad::UUID& client_id,
141 const MinidumpInfo& minidump_info) { 198 const crashpad::UUID& report_id,
199 StabilityReport* report) {
142 DCHECK_NE(base::kInvalidPlatformFile, minidump_platform_file); 200 DCHECK_NE(base::kInvalidPlatformFile, minidump_platform_file);
201 DCHECK(report);
143 202
144 DCHECK_EQ(0U, next_available_byte_); 203 DCHECK_EQ(0U, next_available_byte_);
145 DCHECK(directory_.empty()); 204 DCHECK(directory_.empty());
146 DCHECK_EQ(nullptr, minidump_file_); 205 DCHECK_EQ(nullptr, minidump_file_);
147 206
207 // Ensure the report contains the crasher's product details.
208 ProductDetails product_details = {};
209 if (!GetProductDetails(report->global_data(), &product_details)) {
210 // The report is missing the basic information to determine the affected
211 // version. Ignore the report.
212 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.WriteDumpStatus",
213 FAILED_MISSING_PRODUCT_DETAILS, WRITE_STATUS_MAX);
214 return false;
215 }
216
217 // No need to keep the version details inside the report.
218 report->mutable_global_data()->erase(kStabilityProduct);
219 report->mutable_global_data()->erase(kStabilityChannel);
220 report->mutable_global_data()->erase(kStabilityPlatform);
221 report->mutable_global_data()->erase(kStabilityVersion);
222
148 // We do not own |minidump_platform_file|, but we want to rely on base::File's 223 // We do not own |minidump_platform_file|, but we want to rely on base::File's
149 // API, and so we need to duplicate it. 224 // API, and so we need to duplicate it.
150 HANDLE duplicated_handle; 225 HANDLE duplicated_handle;
151 BOOL duplicate_success = ::DuplicateHandle( 226 BOOL duplicate_success = ::DuplicateHandle(
152 ::GetCurrentProcess(), minidump_platform_file, ::GetCurrentProcess(), 227 ::GetCurrentProcess(), minidump_platform_file, ::GetCurrentProcess(),
153 &duplicated_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); 228 &duplicated_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
154 if (!duplicate_success) 229 if (!duplicate_success) {
230 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.WriteDumpStatus", FAILED,
bcwhite 2017/02/13 18:35:07 To avoid duplicate histogram instantiation code, r
manzagop (departed) 2017/02/13 19:22:36 Ah right! Went with the latter, to limit indentati
231 WRITE_STATUS_MAX);
155 return false; 232 return false;
233 }
156 base::File minidump_file(duplicated_handle); 234 base::File minidump_file(duplicated_handle);
157 DCHECK(minidump_file.IsValid()); 235 DCHECK(minidump_file.IsValid());
158 minidump_file_ = &minidump_file; 236 minidump_file_ = &minidump_file;
159 DCHECK_EQ(0LL, GetFileOffset(minidump_file_)); 237 DCHECK_EQ(0LL, GetFileOffset(minidump_file_));
160 DCHECK(IsFileEmpty(minidump_file_)); 238 DCHECK(IsFileEmpty(minidump_file_));
161 239
162 // Write the minidump, then reset members. 240 // Write the minidump, then reset members.
163 bool success = WriteDumpImpl(report, minidump_info); 241 bool success = WriteDumpImpl(*report, client_id, report_id, product_details);
164 next_available_byte_ = 0U; 242 next_available_byte_ = 0U;
165 directory_.clear(); 243 directory_.clear();
166 minidump_file_ = nullptr; 244 minidump_file_ = nullptr;
167 245
246 WriteStatus status = success ? SUCCESS : FAILED;
bcwhite 2017/02/13 18:35:07 Move this inline below.
manzagop (departed) 2017/02/13 19:22:36 Done.
247 UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.WriteDumpStatus", status,
248 WRITE_STATUS_MAX);
168 return success; 249 return success;
169 } 250 }
170 251
171 bool PostmortemMinidumpWriter::WriteDumpImpl( 252 bool PostmortemMinidumpWriter::WriteDumpImpl(
172 const StabilityReport& report, 253 const StabilityReport& report,
173 const MinidumpInfo& minidump_info) { 254 const crashpad::UUID& client_id,
255 const crashpad::UUID& report_id,
256 const ProductDetails& product_details) {
174 // Allocate space for the header and seek the cursor. 257 // Allocate space for the header and seek the cursor.
175 FilePosition pos = 0U; 258 FilePosition pos = 0U;
176 if (!Allocate(sizeof(MINIDUMP_HEADER), &pos)) 259 if (!Allocate(sizeof(MINIDUMP_HEADER), &pos))
177 return false; 260 return false;
178 if (!SeekCursor(sizeof(MINIDUMP_HEADER))) 261 if (!SeekCursor(sizeof(MINIDUMP_HEADER)))
179 return false; 262 return false;
180 DCHECK_EQ(kHeaderPos, pos); 263 DCHECK_EQ(kHeaderPos, pos);
181 264
182 // Write the proto to the file. 265 // Write the proto to the file.
183 std::string serialized_report; 266 std::string serialized_report;
184 if (!report.SerializeToString(&serialized_report)) 267 if (!report.SerializeToString(&serialized_report))
185 return false; 268 return false;
186 FilePosition report_pos = 0U; 269 FilePosition report_pos = 0U;
187 if (!AppendBytes(serialized_report, &report_pos)) 270 if (!AppendBytes(serialized_report, &report_pos))
188 return false; 271 return false;
189 272
190 // The directory entry for the stability report's stream. 273 // The directory entry for the stability report's stream.
191 RegisterDirectoryEntry(kStabilityReportStreamType, report_pos, 274 RegisterDirectoryEntry(kStabilityReportStreamType, report_pos,
192 serialized_report.length()); 275 serialized_report.length());
193 276
194 // Write mandatory crash keys. These will be read by crashpad and used as 277 // Write mandatory crash keys. These will be read by crashpad and used as
195 // http request parameters for the upload. Keys and values should match 278 // http request parameters for the upload. Keys and values should match
196 // server side configuration. 279 // server side configuration.
197 // TODO(manzagop): use product and version from the stability report. The 280 // TODO(manzagop): use product and version from the stability report. The
198 // current executable's values are an (imperfect) proxy. 281 // current executable's values are an (imperfect) proxy.
199 std::map<std::string, std::string> crash_keys = { 282 std::map<std::string, std::string> crash_keys = {
200 {"prod", minidump_info.product_name + "_Postmortem"}, 283 {"prod", product_details.product + "_Postmortem"},
201 {"ver", minidump_info.version_number}, 284 {"ver", product_details.version},
202 {"channel", minidump_info.channel_name}, 285 {"channel", product_details.channel},
203 {"plat", minidump_info.platform}}; 286 {"plat", product_details.platform}};
204 if (!AppendCrashpadInfo(minidump_info.client_id, minidump_info.report_id, 287 if (!AppendCrashpadInfo(client_id, report_id, crash_keys))
205 crash_keys))
206 return false; 288 return false;
207 289
208 // Write the directory. 290 // Write the directory.
209 FilePosition directory_pos = 0U; 291 FilePosition directory_pos = 0U;
210 if (!AppendVec(directory_, &directory_pos)) 292 if (!AppendVec(directory_, &directory_pos))
211 return false; 293 return false;
212 294
213 // Write the header. 295 // Write the header.
214 MINIDUMP_HEADER header; 296 MINIDUMP_HEADER header;
215 header.Signature = MINIDUMP_SIGNATURE; 297 header.Signature = MINIDUMP_SIGNATURE;
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 uint32_t size) { 480 uint32_t size) {
399 MINIDUMP_DIRECTORY entry = {0}; 481 MINIDUMP_DIRECTORY entry = {0};
400 entry.StreamType = stream_type; 482 entry.StreamType = stream_type;
401 entry.Location.Rva = pos; 483 entry.Location.Rva = pos;
402 entry.Location.DataSize = size; 484 entry.Location.DataSize = size;
403 directory_.push_back(entry); 485 directory_.push_back(entry);
404 } 486 }
405 487
406 } // namespace 488 } // namespace
407 489
408 MinidumpInfo::MinidumpInfo() {} 490 bool WritePostmortemDump(base::PlatformFile minidump_file,
491 const crashpad::UUID& client_id,
492 const crashpad::UUID& report_id,
493 StabilityReport* report) {
494 DCHECK(report);
409 495
410 MinidumpInfo::~MinidumpInfo() {}
411
412 bool WritePostmortemDump(base::PlatformFile minidump_file,
413 const StabilityReport& report,
414 const MinidumpInfo& minidump_info) {
415 PostmortemMinidumpWriter writer; 496 PostmortemMinidumpWriter writer;
416 return writer.WriteDump(minidump_file, report, minidump_info); 497 return writer.WriteDump(minidump_file, client_id, report_id, report);
417 } 498 }
418 499
419 } // namespace browser_watcher 500 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698